声明式编程的好处有:
声明式编程适合封装公共的,不涉及业务逻辑的基础服务,例如远程调用,数据库访问。
下面看一个在Spring中通过声明式编程实现远程访问的Demo,Demo类结构如下:
下面看下代码:
1.EnableRestClients.java
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(RestClientsRegistrar.class) public @interface EnableRestClients { /** * 要扫描的接口类的包名 * * @return */ String[] basePackages() default {}; } 复制代码
2.RestClient.java
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface RestClient { } 复制代码
3.RestClientPath.java
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Documented public @interface RestClientPath { /** * 出于示例简单考虑,只有一个远程访问地址URL * * @return */ String url() default ""; } 复制代码
4.RestClientsRegistrar核心代码
public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { ClassPathScanningCandidateComponentProvider scanner = getScanner(); scanner.setResourceLoader(this.resourceLoader); System.out.println("this.resourceLoader: " + this.resourceLoader); // 添加一个注解过滤器,有RestClient注解的类/接口才继续处理 AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(RestClient.class); scanner.addIncludeFilter(annotationTypeFilter); // 这里的metadata是spring启动类上的注解元数据,下面这一步是获取EnableRestClients注解的属性 Map<String, Object> attributes = metadata.getAnnotationAttributes(EnableRestClients.class.getName()); // 得到EnableRestClients注解上的basePackages属性值,只扫描这些包下的class Set<String> basePackages = new HashSet<>(); for (String pkg : (String[]) attributes.get("basePackages")) { if (StringUtils.hasText(pkg)) { basePackages.add(pkg); } } for (String basePackage : basePackages) { System.out.println("basePackage: " + basePackage); Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage); for (BeanDefinition candidateComponent : candidateComponents) { if (candidateComponent instanceof AnnotatedBeanDefinition) { AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent; // 扫描到的接口/类的注解元数据 AnnotationMetadata annotationMetadata = beanDefinition.getMetadata(); // 得到RestClient注解的属性,这里不需要,因为在代理类中可以通过要代理的类的注解获得 // Map<String, Object> attributes = annotationMetadata // .getAnnotationAttributes(RestClient.class.getCanonicalName()); registerRestClient(registry, annotationMetadata); } } } } /** * * @Description: 注册Bean * @param registry * @param annotationMetadata * @param attributes * * @Author 飞流 * @Date 2019年8月17日 */ private void registerRestClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata) { // 这个类就是扫描到的要处理的类 String className = annotationMetadata.getClassName(); BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(RestClientFactoryBean.class); // 通过此方式给RestClientFactoryBean的成员赋值,将要实现的类传入 definition.addPropertyValue("type", className); // 设置注入方式 definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); AbstractBeanDefinition beanDefinition = definition.getBeanDefinition(); // 通过RestClientFactoryBean生成指定类的实现,这个类就可以通过@Autowired注入了 BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className); BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry); } 复制代码
class RestClientFactoryBean implements FactoryBean<Object>, InitializingBean, ApplicationContextAware { private Class<?> type; private ApplicationContext applicationContext; @Override public void afterPropertiesSet() throws Exception { } @Override public Object getObject() throws Exception { return RestClientProxyFactory.getProxy(type); } @Override public Class<?> getObjectType() { return this.type; } @Override public boolean isSingleton() { return true; } } 复制代码
public class RestClientProxyFactory { public static Object getProxy(Class<?> clazz) { RestClientProxy proxy = new RestClientProxy(); Object newInstanceObject = Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] { clazz }, proxy); return (Object) newInstanceObject; } } 复制代码
public class RestClientProxy implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 获取接口类方法上远程地址 RestClientPath path = method.getAnnotation(RestClientPath.class); String url = path.url(); System.out.println("path.url(): " + url); RestTemplate restTemplate = new RestTemplate(); // 这里arg[0]为方法参数 Object object = restTemplate.postForEntity(url, args[0], method.getReturnType()).getBody(); return object; } } 复制代码
@RestClient public interface UserClient { @RestClientPath(url = "http://localhost:7000/findUserByName") ResponseResult<User> findUserByName(String userName) throws Exception; @RestClientPath(url = "http://localhost:7000/createUser") ResponseResult<Void> createUser(User user) throws Exception; } 复制代码
可以看到这个接口类就是声明式的,通过注解来指定要调用的服务端地址,方法参数以及返回值和远程服务的参数和返回值对应,在实际调用时调用的是动态代理类实现的方法。
9. 接口类的使用
@RestController public class CallUserController { @Autowired private UserClient client; @PostMapping("/callFindUserByName") public ResponseResult<User> callFindUserByName(@RequestBody String userName) throws Exception { ResponseResult<User> response = client.findUserByName(userName); return response; } @PostMapping("/callCreateUser") public ResponseResult<Void> callCreateUser(@RequestBody User user) throws Exception { return client.createUser(user); } } 复制代码
可以看到这里直接使用@Autowired注解来注入接口类,调用的也都是接口方法,而实际调用时会通过Java动态代理调用代理类的方法。
10. 模拟服务端
@RestController public class UserController { @PostMapping("/findUserByName") public ResponseResult<User> findUserByName(@RequestBody String userName) throws Exception { ResponseResult<User> response = new ResponseResult<User>(); User user = new User(); user.setUserName(userName); user.setAge((int) (Math.random() * 50)); response.setResultObject(user); return response; } @PostMapping("/createUser") public ResponseResult<Void> createUser(@RequestBody User user) throws Exception { ResponseResult<Void> response = new ResponseResult<Void>(); response.setResultMsg("Create user success."); return response; } } 复制代码
可以看到服务端参数和返回值和客户端接口类保持一致。
至此就在Spring中实现了声明式编程,完整实例代码扫码加入微信公众号并回复:webfullstack,获取仓库地址。
end.
站点: javashizhan.com/
微信公众号: