聊聊如何優(yōu)雅替換第三方提供的spring bean

前言

前陣子業(yè)務部門接手供方的項目過來運維,在這個項目中姻成,供方提供了一個springboot starter撼泛,但這個starter不滿足業(yè)務部門需求的,業(yè)務部門的研發(fā)本想基于這個starter進行擴展刽肠,但發(fā)現(xiàn)其中有個核心類,用了 @Primary注解免胃,示例形如下

  @Bean
    @Primary
    public ThirdpartyRepository thirdpartyRepository(){
        return new ThirdpartyRepository();
    }

這樣導致他們無法使用他們自定義的類音五,于是業(yè)務部門就找上了我們部門,看我們這邊有沒有什么法子羔沙,今天就來聊聊這個話題躺涝,如何優(yōu)雅的替換第三方提供的spring bean

如何替換第三方提供的spring bean

方案一:通過類替換

具體步驟是將要替換的第三方類拷貝到本項目中,且包名類名和第三方類保持一模一樣扼雏,然后在拷貝后的類中坚嗜,添加自己的業(yè)務邏輯

該方案主要是利用了類的加載順序,即本項目的class會比第三方的class優(yōu)先加載

方案二:利用spring的擴展點進行替換

如果對spring比較了解的話诗充,就會知道一個object對象變成spring bean苍蔬,常規(guī)操作是會通過BeanDefinition轉換成bean對象,因此我們要將第三方的bean替換成我們的bean蝴蜓,我們可以通過修改第三方的BeanDefinition碟绑,那如何修改呢俺猿?我們通過一個具體示例來說明

1、模擬第三方starter

public class ThirdpartyService {

    private ThirdpartyRepository thirdpartyRepository;

    public ThirdpartyService(ThirdpartyRepository thirdpartyRepository) {
        this.thirdpartyRepository = thirdpartyRepository;
    }

    public String getThirdparty(){
        return thirdpartyRepository.getThirdparty();
    }
}

public class ThirdpartyRepository {
    public String getThirdparty() {

        return "Hello Third party Repository";
    }
}

@Configuration
public class ThirdpartyAutoConfiguration {


    @Bean
    @Primary
    public ThirdpartyRepository thirdpartyRepository(){
        return new ThirdpartyRepository();
    }

    @Bean
    public ThirdpartyService thirdpartyService(ThirdpartyRepository thirdpartyRepository){
        return new ThirdpartyService(thirdpartyRepository);
    }
}

2格仲、模擬我們擴展的類

@Repository
public class CustomRepository extends ThirdpartyRepository {

    @Override
    public String getThirdparty() {
        return "Hello Custom Repository";
    }
}

3押袍、先模擬一下測試效果

@SpringBootApplication
public class ThirdpartyTestApplication implements ApplicationRunner {

    @Autowired
    private ThirdpartyService thirdpartyService;

    @Autowired
    private ApplicationContext applicationContext;

    public static void main(String[] args) {
        SpringApplication.run(ThirdpartyTestApplication.class);
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println(thirdpartyService.getThirdparty());
        System.out.println(applicationContext.getBeansOfType(ThirdpartyRepository.class));
    }
}

控制臺輸出如下


22ff35b07a673c2b76b2bf04d4ded642_fdec51b21b0a58a13516a3fb27a0a11b.png

會發(fā)現(xiàn)走的還是第三方的spring bean邏輯

4、修改第三方的spring beanDefinition

public class ThirdpartyBeanReplaceBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private DefaultListableBeanFactory defaultListableBeanFactory;

    private AtomicBoolean isAlreadyReplace = new AtomicBoolean(false);

    private final ThirdpartyBeanReplaceProperty thirdpartyBeanReplaceProperty;

    public ThirdpartyBeanReplaceBeanPostProcessor(ThirdpartyBeanReplaceProperty thirdpartyBeanReplaceProperty) {
        this.thirdpartyBeanReplaceProperty = thirdpartyBeanReplaceProperty;
    }

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        if(thirdpartyBeanReplaceProperty.isBeanReplace() && !CollectionUtils.isEmpty(thirdpartyBeanReplaceProperty.getReplaceBeans()) && !isAlreadyReplace.get()){
            thirdpartyBeanReplaceProperty.getReplaceBeans().forEach(thirdpartyReplaceBeanHolder -> {
                defaultListableBeanFactory.removeBeanDefinition(thirdpartyReplaceBeanHolder.getReplaceBeanName());
                BeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
                beanDefinition.setBeanClassName(thirdpartyReplaceBeanHolder.getReplaceBeanClassName());
                defaultListableBeanFactory.registerBeanDefinition(thirdpartyReplaceBeanHolder.getReplaceBeanName(),beanDefinition);
                logger.info("replace bean:{} to bean:{}",thirdpartyReplaceBeanHolder.getReplaceBeanName(),thirdpartyReplaceBeanHolder.getReplaceBeanClassName());
                isAlreadyReplace.set(true);
            });
        }

        return SmartInstantiationAwareBeanPostProcessor.super.postProcessBeforeInstantiation(beanClass, beanName);

    }

注: 修改BeanDefinition凯肋,需要先執(zhí)行刪除beanName谊惭,再添加的BeanDefinition的步驟,來達到更新的效果侮东,不能直接進行替換圈盔,否則會報錯

 defaultListableBeanFactory.removeBeanDefinition(thirdpartyReplaceBeanHolder.getReplaceBeanName());
                BeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
                beanDefinition.setBeanClassName(thirdpartyReplaceBeanHolder.getReplaceBeanClassName());
                defaultListableBeanFactory.registerBeanDefinition(thirdpartyReplaceBeanHolder.getReplaceBeanName(),beanDefinition);

修改后我們再次測試驗證下

8626101c7c8e7d574a6b5a5688b14e76_5859c680d93248beccf09b3363f71a0f.png

發(fā)現(xiàn)已經(jīng)是走了我們的邏輯

總結

上述提供了2種方案來實現(xiàn)第三方的spring bean替換,其中方案一具有普適性苗桂,即在非spring項目中药磺,也能使用,但是就是不大優(yōu)雅煤伟,尤其當這個類被很多項目引用癌佩,各個項目就得額外加入該類,而且為了讓這個類生效便锨,必須在業(yè)務項目中額外引入和業(yè)務項目關系不是很大的包名围辙。第二種方式比較適用在spring項目中,但就是有局限性放案,只能使用在spring項目中姚建,但相對優(yōu)雅

demo鏈接

https://github.com/lyb-geek/springboot-learning/tree/master/springboot-thirdparty-bean-replace

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市吱殉,隨后出現(xiàn)的幾起案子掸冤,更是在濱河造成了極大的恐慌,老刑警劉巖友雳,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件稿湿,死亡現(xiàn)場離奇詭異,居然都是意外死亡押赊,警方通過查閱死者的電腦和手機饺藤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來流礁,“玉大人涕俗,你說我怎么就攤上這事∩袼В” “怎么了再姑?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長找御。 經(jīng)常有香客問我询刹,道長谜嫉,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任凹联,我火速辦了婚禮,結果婚禮上哆档,老公的妹妹穿的比我還像新娘蔽挠。我一直安慰自己,他們只是感情好瓜浸,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布澳淑。 她就那樣靜靜地躺著,像睡著了一般插佛。 火紅的嫁衣襯著肌膚如雪杠巡。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天雇寇,我揣著相機與錄音氢拥,去河邊找鬼。 笑死锨侯,一個胖子當著我的面吹牛嫩海,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播囚痴,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了祝闻?” 一聲冷哼從身側響起仪缸,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎痴荐,沒想到半個月后血柳,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡蹬昌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年混驰,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片皂贩。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡栖榨,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出明刷,到底是詐尸還是另有隱情婴栽,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布辈末,位于F島的核電站愚争,受9級特大地震影響映皆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜轰枝,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一捅彻、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧鞍陨,春花似錦步淹、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至寿烟,卻和暖如春澈驼,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背筛武。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工缝其, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人畅铭。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓氏淑,卻偏偏與公主長得像,于是被迫代替她去往敵國和親硕噩。 傳聞我的和親對象是個殘疾皇子假残,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容