一、声明式编程的好处
声明式编程的好处有:
- 代码简洁,不需要写很多相同的实现代码
- 对使用者屏蔽了实现细节,使用者只需要声明要做什么,而不关心怎么做。
二、适用场景
声明式编程适合封装公共的,不涉及业务逻辑的基础服务,例如远程调用,数据库访问。
三、Spring中声明式编程的实现
下面看一个在Spring中通过声明式编程实现远程访问的Demo,Demo类结构如下:

- EnableRestClients是一个Spring的启动类注解,用来声明是否要开启此功能,如果有此注解则开启,否则不开启。
- RestClient是要声明的远程接口的类注解,用来声明这些接口是否要实现远程调用。
- RestClientPath是要声明的远程接口的方法注解,用来声明该方法要调用的远程地址。
- RestClientsRegistrar是一个注册类,作用是动态注册远程接口的实现类。
- RestClientFactoryBean是一个工厂Bean,作用是将远程接口的实现作为一个Bean注册到Spring中。
- RestClientProxyFactory是一个代理工厂,用于生成远程接口的代理类。
- 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);
}
- 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;
}
}
- 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;
}
}
- 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;
}
}
- 接口类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.
站点: http://javashizhan.com/
微信公众号: