如何将Spring配置文件已配置的类Mock
来源:互联网 发布:安卓源码网站有哪些 编辑:程序博客网 时间:2024/06/06 15:41
1问题:
手上在做一个历史遗留项目,整个项目有N个模块,我只负责其中一个子模块的功能的维护工作,项目的各个模块是通过spring来将各个模块耦合起来的。
启动项目已经有一个配置文件,并且已经打入了jar包。
为了精简我所描述的场景,避免引入不必要的复杂性。我将不介绍每个模块中的具体实现,重点只描述各个模块之间的依赖关系。
假设我所维护的模块是A,模块A运行需要依赖模块B,模块B是其他人开发的。这个项目在前期是是基于接口开发的,这是一个非常好的前提,这样,我轻而易举就能做些小动作了。以下是这个系统的类图(虽然简单,但是能说明问题)
Spring的配置文件“application-context.xml“是这样写的:
<?xml version="1.0" encoding="UTF-8" ?><beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><!-- 模块配置 --><bean id="moduleB" class="modulemock.DefaultModuleBImpl" /><bean id="moduleA" class="modulemock.DefaultModuleAImpl"><property name="moduleB" ref="moduleB" /></bean></beans>
模块B在配置文件中的实现类已经写死了,在IModuleB的默认实现中是会去链接数据库,这样在测试过程中就需要去配置数据库连接,增加了测试的复杂度,所以很自然地就想到了用Mock技术,需要将ModuleB做成模拟对象。
2 问题解决:
首先很自然地就想到重写写一个配置文件,可以将moduleB的实现类改成模拟类,改成如下这种形式:
<?xml version="1.0" encoding="UTF-8" ?><beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><!-- 模块配置成Mock类 --><bean id="moduleB" class="modulemock.MockModuleB" /><bean id="moduleA" class="modulemock.DefaultModuleAImpl"><property name="moduleB" ref="moduleB" /></bean></beans>以上这个类只能在测试过程中使用,这样正式项目中和测试环境中使用的是两个spring配置文件,这两个配置文件中绝大数配置项是一样的只有ModuleB的配置是不一样的。这样的做法如果项目小的话没有问题,但是项目比较大的就会有问题。
而且,真实的项目中spring的配置文件远远比以上这个demo配置文件复杂许多,而且项目维护过程中经常会更改spring配置文件的配置,这样就需要保证测试环境的spring配置文件和正式项目中的spring配置文件保持同步,实际证明这是一件非常麻烦的事,难免会有遗漏。
现在的问题是,如何能不重写也不改写spring配置文件的前提下,将IModuleB接口Mock化。也就是测试环境和正式环境中只适用同一个 application-context.xml配置文件。办法当然有,那就是利用Spring的org.springframework.beans.factory.config.BeanPostProcessor 接口,利用它在初始化spring容器的过程中会对spring容器中配置的所有Bean会进行拦截:
package org.springframework.beans.factory.config;import org.springframework.beans.BeansException;public interface BeanPostProcessor {Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;}
- postProcessBeforeInitialization 是在spring中配置的bean初始化之前调用的,如果配置的class实现了InitializingBean的afterPropertiesSet的方法,那就会在这bean调用afterPropertiesSet方法前调用该方法
- postProcessAfterInitialization 这个方法会是在InitializingBean调用了afterPropertiesSet这个方法之后来执行
如果配置的bean没有实现InitializingBean这个类的话,无论实现哪个方法都无所了。
要将spring配置项中的对象mock的话,只要在postProcessBeforeInitialization方法上作文章,方法的第一个参数bean是spring容器中配置的bean,spring在实例化bean并且为其设置好依赖之后将这个对象传到post*方法中,原则上需要将这个对象作为返回值返回给容器,但是我们为了mock这个类,我们可以在post*方法中创建一个Mock对象,将这个mock对象作为返回值返回,如果移花接木一般在这里将spring容器中的实现类给改了。
以下是改动之前的项目启动代码:
import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class Main {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml");IModuleA moduleA = context.getBean("moduleA", IModuleA.class);moduleA.execute();}}
现在对他进行改动:import org.springframework.beans.BeansException;import org.springframework.beans.factory.config.BeanPostProcessor;import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class Main {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml") {protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {beanFactory.addBeanPostProcessor(new BeanPostProcessor() {@Overridepublic Object postProcessAfterInitialization(Object bean,String beanName) throws BeansException {return bean;}public Object postProcessBeforeInitialization(Object bean,String beanName) throws BeansException {if ("moduleB".equals(beanName)) {return new MockModuleB();}return bean;}});}};IModuleA moduleA = context.getBean("moduleA", IModuleA.class);moduleA.execute();}}
以上最核心的代码是以下这段:
public Object postProcessBeforeInitialization(Object bean,String beanName) throws BeansException {if ("moduleB".equals(beanName)) {return new MockModuleB();}return bean;}判断被拦截的bean的name属性是否等于“moduleB”,如果相等的话就是要拦截的目标,就实例化一个mock对象,将他返回。
3 总结:
以上利用BeanPostProcessor接口,在spring容器初始化过程中对容器中的bean进行拦截的方法,将容器中的bean进行mock,这个方法的好处是:
- 测试环境和正式环境共用一个spring配置文件,减少配置维护工作。
- spring配置中有任何改动马上可以通过测试验证。
在代码中能够利用spring的机制开创建mock对象,完全依赖于前期代码是面向接口开发的,如果不是这样的话,根本没有办法做Mock对象。在项目中使用面向接口开发,虽然会增加代码维护量,也许在项目前期好处不是很明显,但是在项目维护期会给我们带来方便。类似的例子还有很多,我遇见过一些项目开发前期为了赶进度,方便而放弃了一些软件开发的一些基本原则(ocp原则,里氏替换原则,面向接口开发原则等等。。。),导致项目开发到后期很难维护,造成堆代码的局面。
所以,在项目开发过程中,我们前期我们您可多花一些“无用功”,项目后期我们一定会受益的。
- 如何将Spring配置文件已配置的类Mock
- 如何将Spring配置文件已配置的类Mock
- 如何将Spring的配置文件放到web.xml中
- 利用spring的mock类进行单元测试
- 利用spring的mock类进行单元测试
- 利用spring的mock类进行单元测试
- ssh整合:将hibernate的配置信息,配置到Spring的配置文件中(Spring整合hibernate)
- Spring配置文件(applicationContext.xml如何配置)
- spring配置文件的配置方法
- 如何理解Spring 的配置文件
- Spring配置文件中如何使用外部配置文件配置数据库连接
- Spring Mock--用于Spring 的单元测试
- Spring-Mock--用于Spring 的单元测试
- spring配置文件中 关于继承类的配置
- Spring如何利用XmlBeanFactory类加载bean的配置文件?
- 如何使用多个Spring的xml配置文件(多模块配置)
- 如何使用多个Spring的xml配置文件(多模块配置)
- Spring配置文件关于hibernate的配置
- input输入框定位select选项
- VS2008 "当前不会命中断点。源代码与原始版本不同"解决方法
- C#控件及常用设计整理
- linux中重定向内容到文本文件中
- InvalidateRect只是增加重绘区域,在下次WM_PAINT的时候才生效
- 如何将Spring配置文件已配置的类Mock
- No 120 · android判断是否连接网络
- error RC2135 : file not found: 0
- Textarea 高度自适应 根据内容自适应高度
- Linux 下进行 OCCI ( Oracle C++ Call Interface ) 开发
- select与option:实现表单对应显示和对应显隐
- Libjingle - Google Talk Voice及 P2P 的交互操作函数库
- 安装Windows/Ubuntu双系统
- MyEclipse下如何把现有的jar包打入WEB-INF/lib