Spring覆蓋容器中Bean的注解如何實(shí)現(xiàn)@OverrideBean
Spring覆蓋容器Bean的注解實(shí)現(xiàn)@OverrideBean
項(xiàng)目開發(fā)中,有時(shí)第三方框架會(huì)自動(dòng)注入Bean到Spring容器中,當(dāng)我們有修改對應(yīng)內(nèi)置Bean實(shí)現(xiàn)的需求時(shí),可以采用偷梁換柱的方式來重寫內(nèi)置的Bean,使用這種方式需要注意以下兩點(diǎn):
- 1、對應(yīng)的Bean在其他地方使用時(shí),是基于接口注入的。
- 2、如果不是基于接口注入的Bean,你可能需要同包名同類名的這種方式重寫(可能會(huì)有問題,不推薦)。
從以上2點(diǎn)我們還可以得出一個(gè)結(jié)論,那就是“基于接口編程”的好處。
具體實(shí)現(xiàn)參考一下代碼
(代碼片段,僅供參考,根據(jù)實(shí)際使用場景修改后使用):
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 覆蓋Spring容器中的Bean
*
* @author shanhy
* @date 2021/4/25 13:40
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface OverrideBean {
/**
* 需要替換的 Bean 的名稱
*
* @return
*/
String value();
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
/**
* 重寫B(tài)ean的配置類
*
* @author shanhy
* @date 2021/4/25 13:41
*/
@Configuration
public class OverrideBeanConfiguration implements BeanDefinitionRegistryPostProcessor, BeanFactoryAware {
private static final Logger log = LoggerFactory.getLogger(OverrideBeanConfiguration.class);
private BeanFactory beanFactory;
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
log.debug("searching for classes annotated with @OverrideBean");
// 自定義 Scanner 掃描 classpath 下的指定注解
ClassPathOverrideBeanAnnotationScanner scanner = new ClassPathOverrideBeanAnnotationScanner(registry);
try {
// 獲取包路徑
List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
if (log.isDebugEnabled()) {
for (String p : packages) {
log.debug("Using auto-configuration base package: {}", p);
}
}
// 掃描所有加載的包
scanner.doScan(StringUtils.toStringArray(packages));
} catch (IllegalStateException ex) {
log.debug("could not determine auto-configuration package, automatic OverrideBean scanning disabled.", ex);
}
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory factory) throws BeansException {
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
private static class ClassPathOverrideBeanAnnotationScanner extends ClassPathBeanDefinitionScanner {
ClassPathOverrideBeanAnnotationScanner(BeanDefinitionRegistry registry) {
super(registry, false);
// 設(shè)置過濾器。僅掃描 @OverrideBean
addIncludeFilter(new AnnotationTypeFilter(OverrideBean.class));
}
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
List<String> overrideClassNames = new ArrayList<>();
// 掃描全部 package 下 annotationClass 指定的 Bean
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
GenericBeanDefinition definition;
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (GenericBeanDefinition) holder.getBeanDefinition();
// 獲取類名,并創(chuàng)建 Class 對象
String className = definition.getBeanClassName();
Class<?> clazz = classNameToClass(className);
// 解析注解上的 value
OverrideBean annotation = Objects.requireNonNull(clazz).getAnnotation(OverrideBean.class);
if (annotation == null || annotation.value().length() == 0) {
continue;
}
// 使用當(dāng)前加載的 @OverrideBean 指定的 Bean 替換 value 里指定名稱的 Bean
if (Objects.requireNonNull(getRegistry()).containsBeanDefinition(annotation.value())) {
getRegistry().removeBeanDefinition(annotation.value());
getRegistry().registerBeanDefinition(annotation.value(), definition);
overrideClassNames.add(clazz.getName());
}
}
log.info("found override beans: " + overrideClassNames);
return beanDefinitions;
}
// 反射通過 class 名稱獲取 Class 對象
private Class<?> classNameToClass(String className) {
try {
return Class.forName(className);
} catch (ClassNotFoundException e) {
log.error("create instance failed.", e);
}
return null;
}
}
}
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
idea2020安裝MybatisCodeHelper插件的圖文教程
這篇文章主要介紹了idea2020安裝MybatisCodeHelper插件的方法,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09
Java基礎(chǔ)學(xué)習(xí)之方法的重載知識總結(jié)
今天帶大家來回顧Java基礎(chǔ)知識,文中對Java方法的重載相關(guān)知識作了非常詳細(xì)的介紹,對正在學(xué)習(xí)java的小伙伴們有很好的幫助,需要的朋友可以參考下2021-05-05
Java中生成隨機(jī)數(shù)的實(shí)現(xiàn)方法總結(jié)
這篇文章主要介紹了Java中生成隨機(jī)數(shù)的實(shí)現(xiàn)方法總結(jié),其中多線程并發(fā)的實(shí)現(xiàn)方式尤為exciting,需要的朋友可以參考下2015-11-11
淺談Spring Cloud zuul http請求轉(zhuǎn)發(fā)原理
這篇文章主要介紹了淺談Spring Cloud zuul http請求轉(zhuǎn)發(fā)原理,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-08-08
Mybatis的動(dòng)態(tài)Sql組合模式詳情
這篇文章主要介紹了Mybatis的動(dòng)態(tài)Sql組合模式詳情,這篇文章從組合模式的角度分析了Mybatis動(dòng)態(tài)sql的部分,SqlNode是組合模式的Component接口,更多相關(guān)內(nèi)容需要的小伙伴可以參考一下2022-08-08
詳解Spring 兩種注入的方式(Set和構(gòu)造)實(shí)例
本篇文章主要介紹了Spring 兩種注入的方式(Set和構(gòu)造)實(shí)例,Spring框架主要提供了Set注入和構(gòu)造注入兩種依賴注入方式。有興趣的可以了解一下。2017-02-02
Java實(shí)現(xiàn)NIO聊天室的示例代碼(群聊+私聊)
這篇文章主要介紹了Java實(shí)現(xiàn)NIO聊天室的示例代碼(群聊+私聊),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-05-05

