1. 程式人生 > >Spring擴充套件:替換IOC容器中的Bean元件 -- @Replace註解

Spring擴充套件:替換IOC容器中的Bean元件 -- @Replace註解

## 1、背景:     工作中是否有這樣的場景?一個軟體系統會同時有多個不同版本部署,比如我現在做的IM系統,同時又作為公司的技術輸出給其他銀行,不同的銀行有自己的業務實現(比如登陸驗證、使用者資訊查詢等); 又或者你的工程裡依賴了公司的二方包A,A又依賴了B...這些jar包裡的元件都是通過Spring容器來管理的,如果你想改B中某個類的邏輯,但是又不可能讓架構組的人幫你打一份特殊版本的B;怎麼辦呢?是否可以考慮下直接把Spring容器裡的某個元件(Bean)替換成你自己實現的Bean? ## 2、原理&實現 ### 2.1 先看看Spring開放給我們的擴充套件     Spring框架超強的擴充套件性毋庸置疑,我們可以通過BeanPostProcessor來簡單替換容器中的Bean。 ``` @Component public class MyBeanPostProcessor implements ApplicationContextAware, BeanPostProcessor { private ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (beanName.equals("defaultConfig")) { // 如果遇到需要替換的Bean,我們直接換成自己實現的bean // 這裡的myConfig要繼承自defaultConfig,否則引用的地方會報錯 return applicationContext.getBean("myConfig"); } return bean; } } ``` 優點: * 直接利用Spring原生的擴充套件,可以平滑升級 * 實現簡單,易操作好理解,對於只需要替換少數幾個Bean的情況下推薦這種方式 缺點: * beanName硬編碼在程式碼裡,雖然可以把替換關係配置在properties裡,但是在多版本部署,替換Bean較多時,維護這種關係將是一種負擔 * 僅僅是替換了Bean物件,對於容器中元資料如BeanDefinition等等均是原物件的,存在一定侷限性 ### 2.2 更優雅一點的替換方式     Spring實際上就是一個容器,底層其實就是一個ConcurrentHashMap。如果要替換Map中的Entry,再次呼叫put方法設定相同的key不同的value就可以了。同理,如果要替換Spring容器中的Bean元件,那麼我們重新定義一個同名的Bean並註冊進去就可以了。當然直接申明兩個同名的Bean是過不了Spring中`ClassPathBeanDefinitionScanner`的檢查的,這時候需要我們做一點點擴充套件。 ![](https://img2020.cnblogs.com/blog/631355/202003/631355-20200311113436887-1896545792.png) #### 實現自己的ClassPathBeanDefinitionScanner 目前的想法是直接重寫`checkCandidate`方法,通過判斷Bean的類上是否有@Replace註解,來決定是否通過檢查。 ![](https://img2020.cnblogs.com/blog/631355/202003/631355-20200315182700231-1169329733.png) 依次往上擴充套件就到了`ConfigurationClassPostProcessor`,這是Spring中非常重要的一個容器後置處理器BeanFactoryPostProcessor(上面我們用的是Bean後處理器:BeanPostProcessor),重寫processConfigBeanDefinitions方法就可以引入自己實現的ClassPathBeanDefinitionScanner。 具體細節可以參考:[https://github.com/hiccup234/spring-ext.git](https://github.com/hiccup234/spring-ext.git) ## 3、使用示例     直接在專案中增加如下座標(Maven中央倉庫),目前這個版本是對Spring的5.2.2.RELEASE做擴充套件,新版本的Spring其相對3.X、4.X有部分程式碼變動。 ``` ``` 對Spring Boot中的`SpringApplication`做一點擴充套件,將上面擴充套件的`ConfigurationClassPostProcessor`註冊到容器中。 ![](https://img2020.cnblogs.com/blog/631355/202003/631355-20200320000634938-1282888581.png) 宣告一個自己的類,然後繼承需要替換的Bean的型別(這樣就可以重寫原Bean中的某些方法,從而新增自己的處理邏輯),然後用@Replace("defaultConfig")修飾,如下: ![](https://img2020.cnblogs.com/blog/631355/202003/631355-20200320011649108-521562749.png) 通過ExtSpringApplication啟動,可以看到,實際Spring容器中的Bean已經替換成我們自己實現的Bean元件了。 ![](https://img2020.cnblogs.com/blog/631355/202003/631355-20200320010734038-11831001