工厂方法与FactoryBean
Posted 又见阿郎
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了工厂方法与FactoryBean相关的知识,希望对你有一定的参考价值。
概述
工厂方法是比较常见,常用的一种设计模式。FactoryBean是Spring提供的一种Bean注入IOC容器的方式。
工厂方法
在做日常开发时,一般都会避免直接new对象,而且将new的操作丢给IOC容器,但对于第三方系统的集成,我们不太好直接丢给IOC容器,此时可以通过工厂模式, 提供一个工厂类来实例化具体的接口 实现类,这样,主体对象只需要依赖工厂类,具体使用的实现类有变更的话,只是变更工厂类,而主 体对象不需要做任何变动。 这是一种良好的设计思路。然后我们可以将此工厂对象通过@Component等注解,注入到IOC容器中,由Spring进行管理。
FactoryBean
FactoryBean是Spring容器提供的一种可以扩展容器对象实例化逻辑的接口,请不要将其与容器名称BeanFactory相混淆。FactoryBean,其主语是Bean,定语为Factory,也就是说,它本身与其他注 册到容器的对象一样,只是一个Bean而已,只不过,这种类型的Bean本身就是生产对象的工厂 (Factory)。 当某些对象的实例化过程过于烦琐,通过XML配置过于复杂,使我们宁愿使用Java代码来完成这 个实例化过程的时候,或者,某些第三方库不能直接注册到Spring容器的时候,就可以实现org.springframework.beans.factory.FactoryBean
接口,给出自己的对象实例化逻辑代码。
以Mybatis集成到Spring为例,就使用到了FactoryBean将关键对象SqlSessionFactory、Mapper接口代理对象注入到了IOC容器中,由Spring来接管,使得我们可以通过简单的@Resource等装配机制拿到对应的实例对象。
在实现了FactoryBean接口的类中,我们可以自定义许多的逻辑,然后在注入到IOC容器中,这是很方便强大的功能。
《Spring揭秘》---- 工厂方法与FactoryBean
面向接口编程时,虽然对象可以通过声明接口来避免对特定接口实现类的过渡耦合,但总归需要一种方式将生命依赖接口的对象与接口实现类关联起来。
问题背景:
public class Foo {
private BarInterface barInterface;
public Foo() {
barInstance = new BarInterfaceImpl();
}
}
这样接口与实现类的耦合性很高。
如果BarInterfaceImpl类是我们设计开发的,可以直接通过依赖注入,让容器帮助我们解除接口与实现类的耦合性。但是,有时候我们需要依赖第三方库,需要实例化并使用第三方库中的相关类,可以通过xml方式配置,但却没法使用注解方式。
通常做法是通过使用工厂方法模式,提供一个工厂类来实例化具体的接口实例类,这样,主体对象只需要依赖工厂类,具体使用的实现类有变更的话,只是变更工厂类,而主体对象不需要做任何改动。
- 静态工厂方法
假设某个第三方库发布了BarInterface,为了向使用该接口的客户端对象屏蔽以后可能对BarInterface实现类的变动,同时还提供了一个静态的工厂方法实现类:
public class StaticBarInterfaceFactory {
public static BarInterface getInstance() {
return new BarInterfaceImpl();
}
}
使用以下配置方式,将该静态工厂方法返回的实现注入Foo.
<bean id="foo" class="...Foo"> <property name="barInterface"> <ref bean="bar"/> </property> </bean> <bean id="bar" class="...StaticBarInterfaceFactory" factory-method="getInstance"/>
class指定静态工厂方法类,factory-method指定工厂方法名称,然后容器调用该静态工厂方法类的指定工厂方法,并返回方法调用后的结果,即BarInterfaceImpl的实例。
某些时候,有的工厂类的工厂方法可能需要参数来返回相应的实例,可以通过<constructor-arg>来指定工厂方法需要的参数
public class StaticBarInterfaceFactory
{
public static BarInterface getInstance(Foobar foobar)
{
return new BarInterfaceImpl(foobar);
}
}
配置方式改为如下:
<bean id="foo" class="...Foo"> <property name="barInterface"> <ref bean="bar"/> </property> </bean> <bean id="bar" class="...StaticBarInterfaceFactory" factory-method="getInstance"> <constructor-arg> <ref bean="foobar"/> </constructor-arg> </bean> <bean id="foobar" class="...FooBar"/>
唯一需要注意的就是,针对静态工厂方法实现类的bean定义,使用<constructor-arg>传入的是工厂方法的参数,而不是静态工厂方法实现类的构造方法的参数(静态工厂方法实现类页没有提供显式的构造方法)
2. 非静态工厂方法
public class NonStaticBarInterfaceFactory {
public BarInterface getInstance() {
return new BarInterfaceImpl();
}
}
因为工厂方法为非静态的,只能通过某个NonStaticBarInterfaceFactory实例来调用该方法,配置方法如下:
<bean id="foo" class="...Foo"> <property name="barInterface"> <ref bean="bar"/> </property> </bean> <bean id="barFactory" class="...NonStaticBarInterfaceFactory"/> <bean id="bar" factory-bean="barFactory" factory-method="getInstance"/>
bar的定义需要使用factory-bean来指定工厂方法所在的工厂类实例,而不是通过class属性来指定工厂方法所在类的类型。指定工厂方法名还是使用factory-method指定。如果非静态工厂方法调用时也需要提供参数的话,处理方式是与静态工厂方法相似的,都可以通过<constructor-arg>来指定方法调用参数。
3. FactoryBean
当某些对象的实例化过程过于烦琐,通过XML配置过于复杂,使我们宁愿使用Java代码来完成这个实例化过程的时候,或者,某些第三方库不能直接注册到Spring容器的时候,就可以实现org.spring-framework.beans.factory.FactoryBean接口,给出自己的对象实例化逻辑代码,当然,不使用FactoryBean,而像通常那样实现自定义的工厂方法类也是可以的。FactoryBean接口定义如下:
public interface FactoryBean {
Object getObject() throws Exception;
Class getObjectType();
boolean isSingleton();
}
例如:
import org.joda.time.DateTime;
import org.springframework.beans.factory.FactoryBean;
public class NextDayDateFactoryBean implements FactoryBean {
public Object getObject() throws Exception {
return new DateTime().plusDays(1);
}
public Class getObjectType() {
return DateTime.class;
}
public boolean isSingleton() {
return false;
}
}
public class NextDayDateDisplayer
{
private DateTime dateOfNextDay;
// 相应的setter方法
// ...
}
<bean id="nextDayDateDisplayer" class="...NextDayDateDisplayer"> <property name="dateOfNextDay"> <ref bean="nextDayDate"/> </property> </bean> <bean id="nextDayDate" class="...NextDayDateFactoryBean"> </bean>
NextDayDateDisplayer所声明的dateOfNextDay的类型为DateTime, 而不是NextDayDateFactoryBean.也就是说FactoryBean类型的bean定义,通过正常的id引用,容器返回的是FactoryBean所生产的对象类型,而非FactoryBean实现本身。如果一定要取得FactoryBean本身的话,可以通过在bean定义的id之前加&来达到目的,如:
Object factoryBean = container.getBean("&nextDayDate");
以上是关于工厂方法与FactoryBean的主要内容,如果未能解决你的问题,请参考以下文章
Spring配置bean的方法(工厂方法和Factorybean)