Spring覆蓋容器中Bean的注解如何實(shí)現(xiàn)@OverrideBean
Spring覆蓋容器Bean的注解實(shí)現(xiàn)@OverrideBean
項(xiàng)目開(kāi)發(fā)中,有時(shí)第三方框架會(huì)自動(dòng)注入Bean到Spring容器中,當(dāng)我們有修改對(duì)應(yīng)內(nèi)置Bean實(shí)現(xiàn)的需求時(shí),可以采用偷梁換柱的方式來(lái)重寫(xiě)內(nèi)置的Bean,使用這種方式需要注意以下兩點(diǎn):
- 1、對(duì)應(yīng)的Bean在其他地方使用時(shí),是基于接口注入的。
- 2、如果不是基于接口注入的Bean,你可能需要同包名同類名的這種方式重寫(xiě)(可能會(huì)有問(wèn)題,不推薦)。
從以上2點(diǎn)我們還可以得出一個(gè)結(jié)論,那就是“基于接口編程”的好處。
具體實(shí)現(xiàn)參考一下代碼
(代碼片段,僅供參考,根據(jù)實(shí)際使用場(chǎng)景修改后使用):
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; /** * 重寫(xiě)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è)置過(guò)濾器。僅掃描 @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 對(duì)象 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; } // 反射通過(guò) class 名稱獲取 Class 對(duì)象 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)文章
Java實(shí)現(xiàn)簡(jiǎn)單推箱子游戲
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)推箱子游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-06-06Java基于socket服務(wù)實(shí)現(xiàn)UDP協(xié)議的方法
這篇文章主要介紹了Java基于socket服務(wù)實(shí)現(xiàn)UDP協(xié)議的方法,通過(guò)兩個(gè)簡(jiǎn)單實(shí)例分析了java通過(guò)socket實(shí)現(xiàn)UDP發(fā)送與接收的技巧,需要的朋友可以參考下2015-05-05Java多線程連續(xù)打印abc實(shí)現(xiàn)方法詳解
這篇文章主要介紹了Java多線程連續(xù)打印abc實(shí)現(xiàn)方法詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03Java?9中List.of()的使用示例及注意事項(xiàng)
Java 9引入了一個(gè)新的靜態(tài)工廠方法List.of(),用于創(chuàng)建不可變的列表對(duì)象,這篇文章主要介紹了Java?9中List.of()的使用示例及注意事項(xiàng)的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2025-03-03關(guān)于logBack配置日志文件及編碼配置的問(wèn)題
這篇文章主要介紹了logBack配置日志文件及編碼配置的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07Spring?@Bean注解深入分析源碼執(zhí)行過(guò)程
隨著SpringBoot的流行,我們現(xiàn)在更多采用基于注解式的配置從而替換掉了基于XML的配置,所以本篇文章我們主要探討基于注解的@Bean以及和其他注解的使用2023-01-01Springmvc獲取前臺(tái)請(qǐng)求數(shù)據(jù)過(guò)程解析
這篇文章主要介紹了Springmvc獲取前臺(tái)請(qǐng)求數(shù)據(jù)過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07