之前服务提供方 RpcServer 我们是使用 ApplicationContextAware 来扫描 @RpcService 注解,添加一个注解即可实现服务暴露。现在,我们用 BeanPostProcessor 来实现服务注入,自动将服务实现类注入到被@RpcReference注解标记的接口上。
首先我会先介绍 BeanPostProcessor 的相关信息,以及有所了解的直接跳到第二部分即可。
系列文章:
专栏:从零开始写分布式RPC框架
项目GitHub地址:https://github.com/linshenkx/rpc-netty-spring-boot-starter
手写通用类型负载均衡路由引擎(含随机、轮询、哈希等及其带权形式)
实现 序列化引擎(支持 JDK默认、Hessian、Json、Protostuff、Xml、Avro、ProtocolBuffer、Thrift等序列化方式)
从零写分布式RPC框架 系列 2.0 (1)架构升级
从零写分布式RPC框架 系列 2.0 (2)RPC-Common模块设计实现
从零写分布式RPC框架 系列 2.0 (3)RPC-Server和RPC-Client模块改造
从零写分布式RPC框架 系列 2.0 (4)使用BeanPostProcessor实现自定义@RpcReference注解注入
文章目录
一 BeanPostProcessor 接口
1 定义和功能
官方API文档:
Factory hook that allows for custom modification of new bean instances
即Spring 的一个工厂钩子(其实 Spring 提供一系列的钩子,如 Aware 、InitializingBean、DisposableBean),它是 Spring 提供的对象实例化阶段强有力的扩展点,允许 Spring 在实例化 bean 阶段对其进行定制化修改,比较常见的使用场景是处理标记接口实现类或者为当前对象提供代理实现(例如 AOP)
2 使用方法
实现 BeanPostProcessor 接口并重写 postProcessBeforeInitialization 或 postProcessAfterInitialization 方法。
- postProcessBeforeInitialization 是指bean在初始化之前需要调用的方法
- postProcessAfterInitialization 是指bean在初始化之后需要调用的方法
- postProcessBeforeInitialization和postProcessAfterInitialization方法被调用的时候。
这个时候bean已经被实例化,并且所有该注入的属性都已经被注入,是一个完整的bean
,如果是想要在bean实例化之前进行操作,可以使用 BeanPostProcessor 的一个子接口InstantiationAwareBeanPostProcessor
- 这2个方法的返回值可以是原先生成的实例bean,或者使用wrapper包装这个实例
public interface BeanPostProcessor {
// 初始化之前的操作
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
// 初始化之后的操作
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
3 工作原理
- BeanPostProcessor 的作用域是容器级别的,它只和所在的容器相关 ,当 BeanPostProcessor 完成注册后,它会应用于所有跟它在同一个容器内的 bean 。
- BeanFactory 和 ApplicationContext 对 BeanPostProcessor 的处理不同,ApplicationContext 会自动检测所有实现了 BeanPostProcessor 接口的 bean,并完成注册,但是使用 BeanFactory 容器时则需要手动调用 AbstractBeanFactory#addBeanPostProcessor(BeanPostProcessor beanPostProcessor) 方法来完成注册
- ApplicationContext 的 BeanPostProcessor 支持 Ordered,而 BeanFactory 的 BeanPostProcessor 是不支持的,原因在于ApplicationContext 会对 BeanPostProcessor 进行 Ordered 检测并完成排序,而 BeanFactory 中的 BeanPostProcessor 只跟注册的顺序有关。
4 Bean 生命周期(方法级别)
5 参考资料
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/config/BeanPostProcessor.html
http://wiki.jikexueyuan.com/project/spring/bean-post-processors.html
https://fangjian0423.github.io/2017/06/20/spring-bean-post-processor/
二 实现@RpcReference注解自动注入
1 目标
使用@RpcReference代替手动创建Rpc实现类。
2 思路
通过BeanPostProcessor给@RpcReference注解标记的 Field 注入对应类型的Rpc服务实现类
3 实现
/**
* @version V1.0
* @author: lin_shen
* @date: 18-12-1
* @Description: 目前仅起标识作用
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Service
public @interface RpcReference {
}
/**
* @version V1.0
* @author: lin_shen
* @date: 18-12-1
* @Description: 在 Bean 完成实例化后增加自己的处理逻辑
*/
@Component
@Log4j2
public class RpcClientBeanPostProcessor implements BeanPostProcessor {
@Autowired
private RpcClient rpcClient;
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
processRpcReference(bean);
return bean;
}
private void processRpcReference(Object bean) {
Class beanClass = bean.getClass();
do {
Field[] fields = beanClass.getDeclaredFields();
for (Field field : fields) {
if(field.getAnnotation(RpcReference.class)!=null){
field.setAccessible(true);
try {
field.set(bean, rpcClient.create(field.getType()));
} catch (IllegalAccessException e) {
log.error(e.getMessage());
}
}
}
} while ((beanClass = beanClass.getSuperclass()) != null);
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}