Spring扩展:替换IOC容器中的Bean组件 -- @Replace注解

一、背景:

    工做中是否有这样的场景?一个软件系统会同时有多个不一样版本部署,好比我如今作的IM系统,同时又做为公司的技术输出给其余银行,不一样的银行有本身的业务实现(好比登录验证、用户信息查询等); 又或者你的工程里依赖了公司的二方包A,A又依赖了B...这些jar包里的组件都是经过Spring容器来管理的,若是你想改B中某个类的逻辑,可是又不可能让架构组的人帮你打一份特殊版本的B;怎么办呢?是否能够考虑下直接把Spring容器里的某个组件(Bean)替换成你本身实现的Bean?git

二、原理&实现

2.1 先看看Spring开放给咱们的扩展

    Spring框架超强的扩展性毋庸置疑,咱们能够经过BeanPostProcessor来简单替换容器中的Bean。github

@Component
public class MyBeanPostProcessor implements ApplicationContextAware, BeanPostProcessor {
    private ApplicationContext applicationContext;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (beanName.equals("defaultConfig")) {
            // 若是遇到须要替换的Bean,咱们直接换成本身实现的bean
            // 这里的myConfig要继承自defaultConfig,不然引用的地方会报错
            return applicationContext.getBean("myConfig");
        }
        return bean;
    }
}

优势:spring

  • 直接利用Spring原生的扩展,能够平滑升级
  • 实现简单,易操做好理解,对于只须要替换少数几个Bean的状况下推荐这种方式

缺点:架构

  • beanName硬编码在代码里,虽然能够把替换关系配置在properties里,可是在多版本部署,替换Bean较多时,维护这种关系将是一种负担
  • 仅仅是替换了Bean对象,对于容器中元数据如BeanDefinition等等均是原对象的,存在必定局限性

2.2 更优雅一点的替换方式

    Spring实际上就是一个容器,底层其实就是一个ConcurrentHashMap。若是要替换Map中的Entry,再次调用put方法设置相同的key不一样的value就能够了。同理,若是要替换Spring容器中的Bean组件,那么咱们从新定义一个同名的Bean并注册进去就能够了。固然直接申明两个同名的Bean是过不了Spring中ClassPathBeanDefinitionScanner的检查的,这时候须要咱们作一点点扩展。
app

实现本身的ClassPathBeanDefinitionScanner

目前的想法是直接重写checkCandidate方法,经过判断Bean的类上是否有@Replace注解,来决定是否经过检查。

依次往上扩展就到了ConfigurationClassPostProcessor,这是Spring中很是重要的一个容器后置处理器BeanFactoryPostProcessor(上面咱们用的是Bean后处理器:BeanPostProcessor),重写processConfigBeanDefinitions方法就能够引入本身实现的ClassPathBeanDefinitionScanner。
具体细节能够参考:https://github.com/hiccup234/spring-ext.git框架

三、使用示例

    直接在项目中增长以下坐标(Maven中央仓库),目前这个版本是对Spring的5.2.2.RELEASE作扩展,新版本的Spring其相对3.X、4.X有部分代码变更。ide

<dependency>
    <groupId>top.hiccup</groupId>
    <artifactId>spring-ext</artifactId>
    <version>5.2.2.0-SNAPSHOT</version>
</dependency>

对Spring Boot中的SpringApplication作一点扩展,将上面扩展的ConfigurationClassPostProcessor注册到容器中。

声明一个本身的类,而后继承须要替换的Bean的类型(这样就能够重写原Bean中的某些方法,从而添加本身的处理逻辑),而后用@Replace("defaultConfig")修饰,以下:

经过ExtSpringApplication启动,能够看到,实际Spring容器中的Bean已经替换成咱们本身实现的Bean组件了。
post

相关文章
相关标签/搜索