Author:Willam2004
引言:
FactoryBean:我们在使用Spring过程中一般都是使用基本的的配置,在Spring配置中,还有一种特殊的FactoryBean,这种bean,可以动态的帮我们创建我们需要的bean,如: ProxyFactoryBean,通用的用于获得AOP代理的工厂bean。可以方便帮我们配置AOP的拦截类.
factorybean关键的是接口 org.springframework.beans.factory.FactoryBean,它有两个重要的方法:
Object getObject() throws Exception;
Class getObjectType();
getObject是要实际返回的bean对象
getObjectType是返回Bean对象对应的ObjectType.
原理可以参考:http://dengyin2000.iteye.com/blog/47443
JMock:http://www.jmock.org/
JMock是我们单元测试中经常用到的一个Mock框架.
问题:
单元测试的一个重要难点就是外部接口的依赖,为了保证单元测试的持续有效,我们对外部接口的都要进行Mock.原始的mock,是在单元测试中,直接将接口手动实现一个mock类,在单元测试中再进行注入.但这种缺点是
,如果接口有变更,如新增方法(比较频繁),原有的实现类就需要重新编写.对于依赖二方库,因为更新的延迟,还会导致单元测试报错,需要重新做版本进行发布,耗时耗力.使用jmock就可以避免这个问题,因为你不需要手动再实现这个类,只需要对你关注的方法进行断言就可以了.如代码:
我们需要测试类:
public class RealService {
private MorganService morganService;
/**
* @param morganService the morganService to set
*/
public void setMorganService(MorganService morganService) {
this.morganService = morganService;
}
public void doExecute(String memberId) {
String s = morganService.findMemberById(memberId);
System.out.println(s);
}
}
其中MorganService服务就是我们依赖的接口类,采用jmock的测试如:
public class RealServiceTest extends TestCase {
private RealService service;
/**
* Test method for {@link com.alibaba.RealService#doExecute(java.lang.String)}.
*/
public void testDoExecute() {
Mockery context = new Mockery();
//Mockery是jmock的context,它负责mock对象的创建和mock检查.
final MorganService morganService = context.mock(MorganService.class);
context.checking(new Expectations() {
{
//一次调用morganService的findMemberById方法,返回值为字符串good
oneOf(morganService).findMemberById("test120");
will(returnValue("good"));
}
});
service.setMorganService(morganService);
service.doExecute("test120");
context.assertIsSatisfied(); //检查mock对象有没有正确调用.
}
}
但是目前我们的单元测试,一般是扩展Spring的单元测试进行Bean自动注入,不需要我们手动进行set方法注入,而上面Jmock的使用,需要我们将测试servcie手动调用set方法才能将Mock对象进行注入.
解决方案:
关键问题:我们不能通过配置的方式将mock对象注入到测试的服务类.FactoryBean可以帮我们动态生成我们需要的Bean进行注入.而不用关心到底是什么样的类型.
步骤一:新建MockFactoryBean
public class MockFactoryBean implements FactoryBean {
private String interfaceName;
private boolean expectation = true;
private IExpectaion iexpectation;
/**
* @return the iexpectation
*/
public IExpectaion getIexpectation() {
return iexpectation;
}
/**
* @param iexpectation the iexpectation to set
*/
public void setIexpectation(IExpectaion iexpectation) {
this.iexpectation = iexpectation;
}
/**
* @return the expectation
*/
public boolean isExpectation() {
return expectation;
}
/**
* @param expectation the expectation to set
*/
public void setExpectation(boolean expectation) {
this.expectation = expectation;
}
/**
* @return the interfaceName
*/
public String getInterfaceName() {
return interfaceName;
}
/**
* @param interfaceName the interfaceName to set
*/
public void setInterfaceName(String interfaceName) {
this.interfaceName = interfaceName;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#getObject()
*/
@SuppressWarnings("unchecked")
@Override
public Object getObject() throws Exception {
Mockery context = new Mockery();
final Object o = context.mock(getObjectType());
if (this.isExpectation()) {
context.checking(new Expectations() {
{
//因为Expectations是动态的,所以我将此方法抽出来接口,方便以后的扩展
iexpectation.expectaion(o, this);
}
});
}
return o;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
*/
@SuppressWarnings("unchecked")
@Override
public Class getObjectType() {
// TODO Auto-generated method stub
try {
return Class.forName(interfaceName);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
throw new BeanInstantiationException(this.getClass(), "Can't find the class for use the "
+ getInterfaceName());
}
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#isSingleton()
*/
@Override
public boolean isSingleton() {
return true;
}
}
上面的IExpectaion是一个定义的接口,主要是用来方便自定义断言,它采用泛型的方式进行处理:
public interface IExpectaion {
/**
* @param o
* @param mockFactoryBean
*/
void expectaion(T o, Expectations expectations);
}
这样我们看下我们的applicationContext的配置:
com.alibaba.MorganService
开发人员针对每个外部接口,只需要再实现一个IExpectaion的接口类,进行相应断言就可以进行开发,而不需要再实现全部的接口方法,也避免了接口的方法变更的导致的单元测试错误.
上述方案的改进点:
1.MockFactory其实获取Object的class,可以不需要通过直接的InterfaceName来填写,直接通过iexpectation属性中的泛型参数进行获得.
其他方案
除了以上的方案,还可以通过JTester框架的Mocked注解方式进行处理.这种方式更方便,如:
@SpringApplicationContext("applicationContext.xml")
public class RealServiceJTester extends JTester {
@Mocked
@MockedBean
private MorganService morganService;
@SpringBeanByName
private RealService realService;
@Test
public void testRealService() {
new Expectations() {
{
// some expectation
}
};
realService.doExecute("test100");
}
}
分享到:
相关推荐
FactoryBean:我们在使用Spring过程中一般都是使用基本的的配置,在Spring配置中,还有一种特殊的FactoryBean,这种bean,可以动态的帮我们创建我们需要的bean,如: ProxyFactoryBean,通用的用于获得AOP代理的工厂bean。...
本篇我们将深入探讨`FactoryBean`的最简实现及其在Spring配置文件中的使用,帮助你理解其实例化过程。 `FactoryBean`是Spring框架提供的一种扩展点,它允许我们自定义对象的创建逻辑,而不仅仅是简单的通过`new`...
当你在Spring配置文件中声明一个Bean,并指定它的类型为`FactoryBean`的实现类时,Spring不会直接调用无参构造函数来创建Bean,而是调用`FactoryBean`的方法来获取Bean实例。 首先,我们需要了解`FactoryBean`的...
在标题“spring的FactoryBean增强我们的目标对象.rar”中,提到的核心概念就是如何利用`FactoryBean`来增强目标对象,从而在方法调用时使用代理类而不是原始对象。 `FactoryBean`是Spring中用于对象创建的一种高级...
- 创建代理对象,例如AOP代理,Spring的`AdvisedSupport`类就是一个`FactoryBean`,用于生成动态代理。 - 实现自定义的单例或多例管理。通过`FactoryBean`,我们可以控制Bean的生命周期,比如延迟初始化、按需创建...
这个方法让Spring知道FactoryBean将要创建的对象的类,以便于类型检查和依赖注入。 使用FactoryBean的好处在于,我们可以控制对象的创建过程,包括初始化、装配和其他复杂的逻辑。例如,如果我们有一个复杂的对象...
同时,`FactoryBean`还能与Spring的AOP(面向切面编程)结合,实现更灵活的bean生命周期管理和增强。 总的来说,`FactoryBean`是Spring框架中一个强大的工具,它扩展了Spring容器的能力,使得我们可以自定义对象的...
当我们在配置类上使用`@Import`注解时,Spring会调用`ImportBeanDefinitionRegistrar`的`registerBeanDefinitions`方法,让我们有机会动态地向应用上下文注册bean定义。这对于实现框架级别的扩展,如自动扫描特定的...
通过实现`FactoryBean`,我们可以控制Bean的实例化过程,从而实现更灵活的依赖注入。 首先,让我们来看一下`FactoryBean`的基本使用。在上述示例中,我们创建了一个名为`Car`的简单类,它代表我们要创建的对象。...
而传统的`factoryBean`则是通过实现`FactoryBean`接口来创建bean,这提供了更大的灵活性,但使用起来相对复杂。 结合`@Service`注解,它是Spring Data JPA和其他服务层组件常用的注解。`@Service`通常用于标记业务...
BeanFactory的实现类有DefaultListableBeanFactory、XmlBeanFactory、ApplicationContext等,它们分别从不同的维度扩展了BeanFactory的功能。 BeanFactory的主要方法包括: * getBean(String name):根据Bean的...
如果我们想获取 BeanFactory 的实例,应该使用 getBean("&beanA"),其中"&"符号表示获取 FactoryBean 的实例,而不是 getObject() 方法返回的对象。 在实际工作中,FactoryBean 是非常有用的,可以用来实例化复杂...
3. **接口的实现**: 如果希望为某个接口提供多种实现,可以通过`FactoryBean`动态生成不同的实现类实例。 ### 实例工程`factory-bean-demo` 在`factory-bean-demo`样例工程中,你可以看到一个具体的`FactoryBean`...
在 Spring 中,当配置文件中 <bean> 的 class 属性配置的实现类是 FactoryBean 时,通过 getBean 方法返回的不是 FactoryBean 本身,而是 FactoryBean#getObject() 方法所返回的对象。这相当于 FactoryBean#...
com-spring-ioc-demo:源码主要是学习Spring IOC的原理,以及对Bean的注册及控制,主要运用以下类对...通过FactoryBean结合InvocationHandler关于动态代理invoke()方法的理解5.BeanNameAware 7.BeanFactoryPostPro
1. 创建实现`FactoryBean`接口的类。这个类通常会有一个或多个方法用于创建实际的业务对象。例如: ```java public class MyFactoryBean implements FactoryBean<MyBusinessObject> { @Override public ...
今天我们来了解一下FactoryBean的实现和使用。 首先,让我们从SessionFactory说起。在使用SSH集成开发时,我们经常会在applicationContext.xml中配置Hibernate的信息。下面是一个配置SessionFactory的示例代码: `...
Spring的DataSource实现和FactoryBean模式结合,可以创建动态的、灵活的数据源配置。例如,我们可以使用AbstractRoutingDataSource,这是一个可以根据上下文(如环境变量、线程绑定的属性等)动态选择数据源的...
接着,创建`DynamicDataSource`配置类,以实现数据源的动态切换: ```java @Configuration public class DynamicDataSource { @Autowired private DataSource masterDataSource; @Autowired private ...