亲宝软件园·资讯

展开

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

ocean.wen 人气:1
## 1、背景:     工作中是否有这样的场景?一个软件系统会同时有多个不同版本部署,比如我现在做的IM系统,同时又作为公司的技术输出给其他银行,不同的银行有自己的业务实现(比如登陆验证、用户信息查询等); 又或者你的工程里依赖了公司的二方包A,A又依赖了B...这些jar包里的组件都是通过Spring容器来管理的,如果你想改B中某个类的逻辑,但是又不可能让架构组的人帮你打一份特殊版本的B;怎么办呢?是否可以考虑下直接把Spring容器里的某个组件(Bean)替换成你自己实现的Bean? ## 2、原理&实现 ### 2.1 先看看Spring开放给我们的扩展     Spring框架超强的扩展性毋庸置疑,我们可以通过BeanPostProcessor来简单替换容器中的Bean。 ``` @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原生的扩展,可以平滑升级 * 实现简单,易操作好理解,对于只需要替换少数几个Bean的情况下推荐这种方式 缺点: * beanName硬编码在代码里,虽然可以把替换关系配置在properties里,但是在多版本部署,替换Bean较多时,维护这种关系将是一种负担 * 仅仅是替换了Bean对象,对于容器中元数据如BeanDefinition等等均是原对象的,存在一定局限性 ### 2.2 更优雅一点的替换方式     Spring实际上就是一个容器,底层其实就是一个ConcurrentHashMap。如果要替换Map中的Entry,再次调用put方法设置相同的key不同的value就可以了。同理,如果要替换Spring容器中的Bean组件,那么我们重新定义一个同名的Bean并注册进去就可以了。当然直接申明两个同名的Bean是过不了Spring中`ClassPathBeanDefinitionScanner`的检查的,这时候需要我们做一点点扩展。 ![](https://img2020.cnblogs.com/blog/631355/202003/631355-20200311113436887-1896545792.png) #### 实现自己的ClassPathBeanDefinitionScanner 目前的想法是直接重写`checkCandidate`方法,通过判断Bean的类上是否有@Replace注解,来决定是否通过检查。 ![](https://img2020.cnblogs.com/blog/631355/202003/631355-20200315182700231-1169329733.png) 依次往上扩展就到了`ConfigurationClassPostProcessor`,这是Spring中非常重要的一个容器后置处理器BeanFactoryPostProcessor(上面我们用的是Bean后处理器:BeanPostProcessor),重写processConfigBeanDefinitions方法就可以引入自己实现的ClassPathBeanDefinitionScanner。 具体细节可以参考:[https://github.com/hiccup234/spring-ext.git](https://github.com/hiccup234/spring-ext.git) ## 3、使用示例     直接在项目中增加如下坐标(Maven中央仓库),目前这个版本是对Spring的5.2.2.RELEASE做扩展,新版本的Spring其相对3.X、4.X有部分代码变动。 ``` ``` 对Spring Boot中的`SpringApplication`做一点扩展,将上面扩展的`ConfigurationClassPostProcessor`注册到容器中。 ![](https://img2020.cnblogs.com/blog/631355/202003/631355-20200320000634938-1282888581.png) 声明一个自己的类,然后继承需要替换的Bean的类型(这样就可以重写原Bean中的某些方法,从而添加自己的处理逻辑),然后用@Replace("defaultConfig")修饰,如下: ![](https://img2020.cnblogs.com/blog/631355/202003/631355-20200320011649108-521562749.png) 通过ExtSpringApplication启动,可以看到,实际Spring容器中的Bean已经替换成我们自己实现的Bean组件了。 ![](https://img2020.cnblogs.com/blog/631355/202003/631355-20200320010734038-1183100123.png)

加载全部内容

相关教程
猜你喜欢
用户评论