工厂方法与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方式配置,但却没法使用注解方式。

  通常做法是通过使用工厂方法模式,提供一个工厂类来实例化具体的接口实例类,这样,主体对象只需要依赖工厂类,具体使用的实现类有变更的话,只是变更工厂类,而主体对象不需要做任何改动。

  1.  静态工厂方法

假设某个第三方库发布了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中的工厂FactoryBean

Spring配置bean的方法(工厂方法和Factorybean)

factory方法模式

Spring 中 BeanFactory 与 FactoryBean 的区别

spring注解第05课 FactoryBean

4.spriing:Bean的生命周期/工厂方法配置Bean/FactoryBean