前言
前陣子業(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));
}
}
控制臺輸出如下
會發(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);
修改后我們再次測試驗證下
發(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