Spring擴展:替換IOC容器中的Bean組件 -- @Replace註解

一、背景:

    工做中是否有這樣的場景?一個軟件系統會同時有多個不一樣版本部署,好比我如今作的IM系統,同時又做爲公司的技術輸出給其餘銀行,不一樣的銀行有本身的業務實現(好比登錄驗證、用戶信息查詢等); 又或者你的工程裏依賴了公司的二方包A,A又依賴了B...這些jar包裏的組件都是經過Spring容器來管理的,若是你想改B中某個類的邏輯,可是又不可能讓架構組的人幫你打一份特殊版本的B;怎麼辦呢?是否能夠考慮下直接把Spring容器裏的某個組件(Bean)替換成你本身實現的Bean?git

二、原理&實現

2.1 先看看Spring開放給咱們的擴展

    Spring框架超強的擴展性毋庸置疑,咱們能夠經過BeanPostProcessor來簡單替換容器中的Bean。github

@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

  • 直接利用Spring原生的擴展,能夠平滑升級
  • 實現簡單,易操做好理解,對於只須要替換少數幾個Bean的狀況下推薦這種方式

缺點:架構

  • beanName硬編碼在代碼裏,雖然能夠把替換關係配置在properties裏,可是在多版本部署,替換Bean較多時,維護這種關係將是一種負擔
  • 僅僅是替換了Bean對象,對於容器中元數據如BeanDefinition等等均是原對象的,存在必定侷限性

2.2 更優雅一點的替換方式

    Spring實際上就是一個容器,底層其實就是一個ConcurrentHashMap。若是要替換Map中的Entry,再次調用put方法設置相同的key不一樣的value就能夠了。同理,若是要替換Spring容器中的Bean組件,那麼咱們從新定義一個同名的Bean並註冊進去就能夠了。固然直接申明兩個同名的Bean是過不了Spring中ClassPathBeanDefinitionScanner的檢查的,這時候須要咱們作一點點擴展。
app

實現本身的ClassPathBeanDefinitionScanner

目前的想法是直接重寫checkCandidate方法,經過判斷Bean的類上是否有@Replace註解,來決定是否經過檢查。

依次往上擴展就到了ConfigurationClassPostProcessor,這是Spring中很是重要的一個容器後置處理器BeanFactoryPostProcessor(上面咱們用的是Bean後處理器:BeanPostProcessor),重寫processConfigBeanDefinitions方法就能夠引入本身實現的ClassPathBeanDefinitionScanner。
具體細節能夠參考:https://github.com/hiccup234/spring-ext.git框架

三、使用示例

    直接在項目中增長以下座標(Maven中央倉庫),目前這個版本是對Spring的5.2.2.RELEASE作擴展,新版本的Spring其相對3.X、4.X有部分代碼變更。ide

<dependency>
    <groupId>top.hiccup</groupId>
    <artifactId>spring-ext</artifactId>
    <version>5.2.2.0-SNAPSHOT</version>
</dependency>

對Spring Boot中的SpringApplication作一點擴展,將上面擴展的ConfigurationClassPostProcessor註冊到容器中。

聲明一個本身的類,而後繼承須要替換的Bean的類型(這樣就能夠重寫原Bean中的某些方法,從而添加本身的處理邏輯),而後用@Replace("defaultConfig")修飾,以下:

經過ExtSpringApplication啓動,能夠看到,實際Spring容器中的Bean已經替換成咱們本身實現的Bean組件了。
post

相關文章
相關標籤/搜索