Spring覆蓋容器中Bean的注解如何實現(xiàn)@OverrideBean
更新時間:2025年01月06日 10:38:45 作者:catoop
文章介紹了在項目開發(fā)中如何通過偷梁換柱的方式重寫Spring容器中的內(nèi)置Bean,并指出了需要注意的兩點:1. 對應(yīng)的Bean應(yīng)基于接口注入;2. 如果不是基于接口注入,可以使用同包名同類名的方式重寫(可能存在潛在問題,不推薦),文章還強(qiáng)調(diào)了“基于接口編程”的好處
Spring覆蓋容器Bean的注解實現(xiàn)@OverrideBean
項目開發(fā)中,有時第三方框架會自動注入Bean到Spring容器中,當(dāng)我們有修改對應(yīng)內(nèi)置Bean實現(xiàn)的需求時,可以采用偷梁換柱的方式來重寫內(nèi)置的Bean,使用這種方式需要注意以下兩點:
- 1、對應(yīng)的Bean在其他地方使用時,是基于接口注入的。
- 2、如果不是基于接口注入的Bean,你可能需要同包名同類名的這種方式重寫(可能會有問題,不推薦)。
從以上2點我們還可以得出一個結(jié)論,那就是“基于接口編程”的好處。
具體實現(xiàn)參考一下代碼
(代碼片段,僅供參考,根據(jù)實際使用場景修改后使用):
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é)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java基于socket服務(wù)實現(xiàn)UDP協(xié)議的方法
這篇文章主要介紹了Java基于socket服務(wù)實現(xiàn)UDP協(xié)議的方法,通過兩個簡單實例分析了java通過socket實現(xiàn)UDP發(fā)送與接收的技巧,需要的朋友可以參考下2015-05-05Java多線程連續(xù)打印abc實現(xiàn)方法詳解
這篇文章主要介紹了Java多線程連續(xù)打印abc實現(xiàn)方法詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-03-03Springmvc獲取前臺請求數(shù)據(jù)過程解析
這篇文章主要介紹了Springmvc獲取前臺請求數(shù)據(jù)過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-07-07