Spring覆盖容器中Bean的注解如何实现@OverrideBean
更新时间:2025年01月06日 10:38:45 作者:catoop
文章介绍了在项目开发中如何通过偷梁换柱的方式重写Spring容器中的内置Bean,并指出了需要注意的两点:1. 对应的Bean应基于接口注入;2. 如果不是基于接口注入,可以使用同包名同类名的方式重写(可能存在潜在问题,不推荐),文章还强调了“基于接口编程”的好处
Spring覆盖容器Bean的注解实现@OverrideBean
项目开发中,有时第三方框架会自动注入Bean到Spring容器中,当我们有修改对应内置Bean实现的需求时,可以采用偷梁换柱的方式来重写内置的Bean,使用这种方式需要注意以下两点:
- 1、对应的Bean在其他地方使用时,是基于接口注入的。
- 2、如果不是基于接口注入的Bean,你可能需要同包名同类名的这种方式重写(可能会有问题,不推荐)。
从以上2点我们还可以得出一个结论,那就是“基于接口编程”的好处。
具体实现参考一下代码
(代码片段,仅供参考,根据实际使用场景修改后使用):
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; /** * 重写Bean的配置类 * * @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); // 设置过滤器。仅扫描 @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(); // 获取类名,并创建 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; } // 使用当前加载的 @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; } } }
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
相关文章
mybatis中foreach报错:_frch_item_0 not found的解决方法
这篇文章主要给大家介绍了mybatis中foreach报错:_frch_item_0 not found的解决方法,文章通过示例代码介绍了详细的解决方法,对大家具有一定的参考学习价值,需要的朋友们下面来一起看看吧。2017-06-06SpringBoot集成tensorflow实现图片检测功能
TensorFlow名字的由来就是张量(Tensor)在计算图(Computational Graph)里的流动(Flow),它的基础就是前面介绍的基于计算图的自动微分,本文将给大家介绍Spring Boot集成tensorflow实现图片检测功能,需要的朋友可以参考下2024-06-06SpringBoot整合Guava Cache实现全局缓存的示例代码
这篇文章主要介绍了SpringBoot整合Guava Cache实现全局缓存,Guava Cache是Google Guava库中的一个模块,提供了基于内存的本地缓存实现,文中介绍了SpringBoot整合使用Guava Cache的具体步骤,需要的朋友可以参考下2024-03-03Spring cloud如何实现FeignClient指定Zone调用
这篇文章主要介绍了Spring cloud如何实现FeignClient指定Zone调用方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教2022-03-03
最新评论