1. 程式人生 > >從零寫分散式RPC框架 系列 2.0 (4)使用BeanPostProcessor實現自定義@RpcReference註解注入

從零寫分散式RPC框架 系列 2.0 (4)使用BeanPostProcessor實現自定義@RpcReference註解注入

之前服務提供方 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 方法。

  1. postProcessBeforeInitialization 是指bean在初始化之前需要呼叫的方法
  2. postProcessAfterInitialization 是指bean在初始化之後需要呼叫的方法
  3. postProcessBeforeInitialization和postProcessAfterInitialization方法被呼叫的時候。這個時候bean已經被例項化,並且所有該注入的屬性都已經被注入,是一個完整的bean,如果是想要在bean例項化之前進行操作,可以使用 BeanPostProcessor 的一個子介面InstantiationAwareBeanPostProcessor
  4. 這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 工作原理

  1. BeanPostProcessor 的作用域是容器級別的,它只和所在的容器相關 ,當 BeanPostProcessor 完成註冊後,它會應用於所有跟它在同一個容器內的 bean 。
  2. BeanFactory 和 ApplicationContext 對 BeanPostProcessor 的處理不同,ApplicationContext 會自動檢測所有實現了 BeanPostProcessor 介面的 bean,並完成註冊,但是使用 BeanFactory 容器時則需要手動呼叫 AbstractBeanFactory#addBeanPostProcessor(BeanPostProcessor beanPostProcessor) 方法來完成註冊
  3. 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;
    }

}