工厂方法与 FactoryBean(Spring对工厂模式的XML支持)

工厂方法与 FactoryBean

引子

直接依赖接口实现类的类Foo

public class Foo 
{ 
private BarInterface barInstance; 
  public Foo()  { 
  // 我们应该避免这样做
// instance = new BarInterfaceImpl(); 
  } 
  // ... 
复制代码

使用了工厂方法模式的Foo类可能定义:

public class Foo 
{ 
private BarInterface barInterface; 
  public Foo() 
  { 
  // barInterface = BarInterfaceFactory.getInstance(); 
  // 或者  // barInterface = new BarInterfaceFactory().getInstance(); 
  } 
... 

复制代码

此时接口BarInterfaceBarInterfaceImpl解耦,BarInterfaceBarInterfaceFactory耦合,实现了替换BarInterface实现类只需要替换BarInterfaceFactory持有的实现类即可。

静态工厂方法(Static Factory Method)

不带参数的静态工厂方法:

静态工厂类:

public class StaticBarInterfaceFactory 
{ 
  public static BarInterface getInstance() 
  { 
   return new BarInterfaceImpl(); 
  } 
} 
复制代码

Foo注入BarInterface的XML配置:

<bean id="foo" class="...Foo"> 
  <property name="barInterface"> 
    <ref bean="bar"/> 
  </property> 
</bean> 

<bean id="bar" class="...StaticBarInterfaceFactory" factory-method="getInstance"/> 
复制代码

其中,barInterface的注入方式为setter注入,为Foo类实例注入了BarInterfaceImpl的实例。且此处的id="bar"的bean指的是通过StaticBarInterfaceFactorygetInstance方法获得的BarInterfaceImpl实例。

注意id="bar"的bean属性factory-methodclass的作用

带参数的静态工厂方法:

静态方法带参数的静态工厂类:

public class StaticBarInterfaceFactory 
{ 
  public static BarInterface getInstance(Foobar foobar) 
  { 
  return new BarInterfaceImpl(foobar); 
  } 
} 
复制代码

为Foo注入 BarInterface 的XML配置:

<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"/> 
复制代码

具体参数的使用在BarInterfaceImpl中实现,同上,此处的id="bar"的bean指的是通过 StaticBarInterfaceFactory 的 getInstance 方法获得的 BarInterfaceImpl 实例,构造参数foobar也是传递给BarInterfaceImp的。

注意<constructor-arg>节点的作用

非静态工厂方法(Instance Factory Method)

非静态方法工厂类:

public class NonStaticBarInterfaceFactory 
{ 
public BarInterface getInstance() 
  { 
  return new BarInterfaceImpl(); 
} 
  ... 
复制代码

为Foo注入 BarInterface 的XML配置:

<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"/> 
复制代码

此处的工厂类作为普通bean注册到Spring容器中,而不是直接调用静态方法(需要实例化才能调用实例方法)。
如果非静态工厂方法调用时也需要提供参数的话,处理方式是与静态的工厂方法相似的,都可以通过<constructor-arg>来指定方法调用参数。

注意factory-bean属性的作用。

FactoryBean

FactoryBean是Spring容器提供的一种可以扩展容器对象实例化逻辑的接口,请不要将其与容器名称BeanFactory相混淆。它的实现类是专用于生产对象的bean。
当某些对象的实例化过程过于烦琐,通过XML配置过于复杂,使我们宁愿使用Java代码来完成这 个实例化过程的时候,或者,某些第三方库不能直接注册到Spring容器的时候,就可以实现org.spring.framework.beans.factory.FactoryBean接口,给出自己的对象实例化逻辑代码。

FactoryBean定义:

public interface FactoryBean { 
    //返回产品类对象
  Object getObject() throws Exception; 
  //返回产品类类型
  Class getObjectType();
  //返回产品类scope
  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; 
  } 
} 
复制代码

NextDayDateFactoryBean注册到Spring容器:

<bean id="nextDayDateDisplayer" class="...NextDayDateDisplayer"> 
    <property name="dateOfNextDay"> 
        <ref bean="nextDayDate"/> 
    </property> 
</bean>  

<bean id="nextDayDate" class="...NextDayDateFactoryBean"> 
</bean> 
复制代码

注意id="nextDayDate"的bean没有使用factory-method属性就完成了依赖对象的生产,这里是FactoryBean接口的作用。

NextDayDateDisplayer类定义:

public class NextDayDateDisplayer 
{ 
  private DateTime dateOfNextDay; 
  // 相应的setter方法
  // ... 
} 
复制代码

由此,我们实现了NextDayDateDisplayer不依赖NextDayDateFactoryBean即可得到依赖的DateTime实例,即Spring容器通过工厂模式实现了类依赖注入。

使用&获取FactoryBean的实例演示:

//以下所有assert均通过

//获取工厂产品类DateTime
Object nextDayDate = container.getBean("nextDayDate"); 
assertTrue(nextDayDate instanceof DateTime);

//获取FactoryBean类的对象本身
Object factoryBean = container.getBean("&nextDayDate"); 
assertTrue(factoryBean instanceof FactoryBean); 
assertTrue(factoryBean instanceof NextDayDateFactoryBean); 

//通过上一步获得的FactoryBean获取工厂产品类DateTime的对象
Object factoryValue = ((FactoryBean)factoryBean).getObject(); 
assertTrue(factoryValue instanceof DateTime); 

assertNotSame(nextDayDate, factoryValue); 
assertEquals(((DateTime)nextDayDate).getDayOfYear(),((DateTime)factoryValue).getDayOfYear()); 
复制代码

Spring容器使用FactoryBean案例:

  • JndiObjectFactoryBean
  • LocalSessionFactoryBean
  • SqlMapClientFactoryBean
  • ProxyFactoryBean
  • TransactionProxyFactoryBean

转载于:https://juejin.im/post/5c1ca2f1f265da61736a432f

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值