如何动态替换Spring容器中的Bean
动态替换Spring容器中的Bean
原因
最近在编写单测时,发现使用 Mock 工具预定义 Service 中方法的行为特别难用,而且无法精细化的实现自定义的行为,因此想要在 Spring 容器运行过程中使用自定义 Mock 对象,该对象能够代替实际的 Bean 的给定方法。
方案
创建一个 Mock 注解,并且在 Spring 容器注册完所有的 Bean 之后,解析 classpath 下所有引入该 Mock 注解的类,使用 Mock 注解标记的 Bean 替换注解中指定名称的 Bean。
这种方式类似于 mybatis-spring 动态解析 @Mapper 注解的方法(MapperScannerRegistrar 实现了@Mapper 注解的扫描),但是不一样的是 mybatis-spring 使用工厂类替换接口类,而我们是用 Mock 的 Bean 替换实际的 Bean。
实现
创建 Mock 注解
在 Spring 容器注册完所有的 Bean 后,解析 classpath 下引入 @FakeBeanFor 注解的类,使用 @FakeBeanFor 注解标记的 Bean 替换 value 中指定名称的 Bean。
有点儿不一样的是这是一个配置类,将它放置到 Spring 的自动扫描路径上,就可以自动扫描 classpath 下 @FakeBeanFor 指定的类,并将其加载为 BeanDefinition。
在 FakeBeanConfiguration 上还配置了 ConditionalOnExpression,这样就可以只在单测环境下的 application.properties 文件中设置指定条件使得该 Configuration 生效。
注意:
- 这里 unitcases.enable.fake:true 默认开启了替换,如果想要默认关闭则需要设置 unitcases.enable.fake:false,并且在单测环境的 application.properties 文件设置 unitcases.enable.fake=true。
举例
假设在容器中定义如下 Service:
在单测环境下希望能够改变它的行为,但是又不想修改这个类本身,则可以使用 @FakeBeanFor 注解:
通过继承实际的 Service,并覆盖 Service 的原始方法,修改其行为。在单测中可以这样使用:
总结:通过自定义的 Mock 对象动态替换实际的 Bean 可以实现单测环境下比较难以使用 Mock 框架实现的功能,如将原本的异步调用逻辑修改为同步调用,避免单测完成时,异步调用还未执行完成的场景。
Spring中bean替换问题
需求:通过配置文件,能够使得新的一个service层类替代jar包中原有的类文件。
项目原因,引用了一些成型产品的jar包,已经不能对其进行修改了。
故,考虑采用用新的类替换jar包中的类。
实现思路:在配置文件中配置新老类的全类名,读取配置文件后,通过spring初始化bean的过程中,移除spring容器中老类的bean对象,手动注册新对象进去,bean名称和老对象一致即可。
jar包中的老对象是通过@Service注册到容器中的。
新的类因为是手动配置,不需要添加任何注解。
实现的方法如下:
配置文件中的格式采用下面的样式 :
在启动的时候,会找到容器中的老的bean,将其remove掉,然后手动注册新的bean到容器中。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程学习网。