转载

重识Java动态代理(二)Spring中声明式编程实现

声明式编程的好处有:

  1. 代码简洁,不需要写很多相同的实现代码
  2. 对使用者屏蔽了实现细节,使用者只需要声明要做什么,而不关心怎么做。

二、适用场景

声明式编程适合封装公共的,不涉及业务逻辑的基础服务,例如远程调用,数据库访问。

三、Spring中声明式编程的实现

下面看一个在Spring中通过声明式编程实现远程访问的Demo,Demo类结构如下:

重识Java动态代理(二)Spring中声明式编程实现
  1. EnableRestClients是一个Spring的启动类注解,用来声明是否要开启此功能,如果有此注解则开启,否则不开启。
  2. RestClient是要声明的远程接口的类注解,用来声明这些接口是否要实现远程调用。
  3. RestClientPath是要声明的远程接口的方法注解,用来声明该方法要调用的远程地址。
  4. RestClientsRegistrar是一个注册类,作用是动态注册远程接口的实现类。
  5. RestClientFactoryBean是一个工厂Bean,作用是将远程接口的实现作为一个Bean注册到Spring中。
  6. RestClientProxyFactory是一个代理工厂,用于生成远程接口的代理类。
  7. RestClientProxy是远程接口的代理类,调用远程接口方法时,实际调用的是代理类。

下面看下代码:

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);
	}
复制代码
  1. RestClientFactoryBean.java
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;
	}
}
复制代码
  1. RestClientProxyFactory.java
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;
	}
}
复制代码
  1. RestClientProxy.java
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;
	}
}
复制代码
  1. 接口类UserClient.java
@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/

微信公众号:

重识Java动态代理(二)Spring中声明式编程实现
原文  https://juejin.im/post/5d57be715188251aa51cd80f
正文到此结束
Loading...