BeanFactory与FactoryBean

1.BeanFactory是什么

Spring的IoC容器是一个IoC Service Provider,但是,这只是它被冠以IoC之名的部分原因,我们不能忽略的是“容器”。Spring的IoC容器是一个提供IoC支持的轻量级容器,除了基本的IoC支持,它作为轻量级容器还提供了IoC之外的支持。如在Spring的IoC容器之上,Spring还提供了相应的AOP框架支持、企业级服务集成等服务。Spring的IoC容器和IoC Service Provider所提供的服务之间存在一定的交集,二者的关系如图所示。
Spring的IoC容器和IoC Service Provider之间的关系
Spring提供了两种容器类型:BeanFactoryApplicationContext

  • BeanFactory。基础类型IoC容器,提供完整的IoC服务支持。如果没有特殊指定,默认采用延迟初始化策略(lazy-load)。所以,相对来说,容器启动初期速度较快,所需要的资源有限。对于资源有限,并且功能要求不是很严格的场景,BeanFactory是比较合适的IoC容器选择。
  • ApplicationContext。ApplicationContext在BeanFactory的基础上构建,是相对比较高级的容器实现,除了拥有BeanFactory的所有功能之外,ApplicationContext还提供了其他高级特性,比如事件发布、国际化信息支持等,这些会在后面详述。ApplicationContext所管理的对象,在该类型容器启动之后,默认全部初始化并绑定完成。所以,相对于BeanFactory来说,ApplicationContext要求更多的系统资源,同时,因为在启动时就完成所有初始化,容器启动时间较之BeanFactory也会长一些。在那些系统资源充足,并且要求更多功能的场景中,
    ApplicationContext类型的容器是比较合适的选择。

通过图4-2,我们可以对BeanFactory和ApplicationContext之间的关系有一个更清晰的认识。
在这里插入图片描述
BeanFactory,顾名思义,就是生产Bean的工厂。当然,严格来说,这个“生产过程”可能不像说起来那么简单。既然Spring框架提倡使用POJO,那么把每个业务对象看作一个JavaBean对象,或许更容易理解为什么Spring的IoC基本容器会起这么一个名字。作为Spring提供的基本的IoC容器,BeanFactory可以完成作为IoC Service Provider的所有职责,包括业务对象的注册和对象间依赖关系的绑定。

BeanFactory就像一个汽车生产厂。你从其他汽车零件厂商或者自己的零件生产部门取得汽车零件送入这个汽车生产厂,最后,只需要从生产线的终点取得成品汽车就可以了。相似地,将应用所需的所有业务对象交给BeanFactory之后,剩下要做的,就是直接从BeanFactory取得最终组装完成并且可用的对象。至于这个最终业务对象如何组装,你不需要关心,BeanFactory会帮你搞定。

所以,对于客户端来说,与BeanFactory打交道其实很简单。最基本地,BeanFactory肯定会公开一个取得组装完成的对象的方法接口,就像代码清单4-1中真正的BeanFactory的定义所展示的那样。

public interface BeanFactory {

	String FACTORY_BEAN_PREFIX = "&";

	Object getBean(String name) throws BeansException;

	<T> T getBean(String name, Class<T> requiredType) throws BeansException;

	Object getBean(String name, Object... args) throws BeansException;

	<T> T getBean(Class<T> requiredType) throws BeansException;

	<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;

	<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);

	<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);

	boolean containsBean(String name);

	boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

	boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

	boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;

	boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;

	@Nullable
	Class<?> getType(String name) throws NoSuchBeanDefinitionException;

	@Nullable
	Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;

	String[] getAliases(String name);

}

上面代码中的方法基本上都是查询相关的方法,例如,取得某个对象的方法(getBean)、查询某个对象是否存在于容器中的方法(containsBean),或者取得某个bean的状态或者类型的方法等。因为通常情况下,对于独立的应用程序,只有主入口类才会跟容器的API直接耦合。

FactoryBean

FactoryBean是Spring容器提供的一种可以扩展容器对象实例化逻辑的接口,请不要将其与容器名称BeanFactory相混淆。FactoryBean,其主语是Bean,定语为Factory,也就是说,它本身与其他注册到容器的对象一样,只是一个Bean而已,只不过,这种类型的Bean本身就是生产对象的工厂(Factory)。

当某些对象的实例化过程过于烦琐,通过XML配置过于复杂,使我们宁愿使用Java代码来完成这个实例化过程的时候,或者,某些第三方库不能直接注册到Spring容器的时候,就可以实org.springframework.beans.factory.FactoryBean接口,给出自己的对象实例化逻辑码。当然,不使用FactoryBean,而像通常那样实现自定义的工厂方法类也是可以的。不过,FactoryBean可是Spring提供的对付这种情况的“制式装备”哦!

要实现并使 用自己的 FactoryBean其实很简单, org.springframework.beans.factory.FactoryBean只定义了三个方法,如以下代码所示:

public interface FactoryBean<T> {

	String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";

	@Nullable
	T getObject() throws Exception;

	@Nullable
	Class<?> getObjectType();

	default boolean isSingleton() {
		return true;
	}

}

getObject()方法会返回该FactoryBean“生产”的对象实例,我们需要实现该方法以给出自己的对象实例化逻辑;getObjectType()方法仅返回getObject()方法所返回的对象的类型,如果预先无法确定,则返回null;isSingleton()方法返回结果用于表明,工厂方法(getObject())所“生产”的对象是否要以singleton形式存在于容器中。如果以singleton形式存在,则返回true,否则返回false;

如果我们想每次得到的日期都是第二天,可以实现一个如下所示的FactoryBean。

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,只需要如下这样将其注册到容器即可:

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

配置上看不出与平常的bean定义有何不同,不过,只有当我们看到NextDayDateDisplayer的定义的时候,才会知道FactoryBean的魔力到底在哪。NextDayDateDisplayer的定义如下:

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

看到了嘛?NextDayDateDisplayer所声明的依赖dateOfNextDay的类型为DateTime,而不是NextDayDateFactoryBean。也就是说FactoryBean类型的bean定义,通过正常的id引用,容器返回的是FactoryBean所“生产”的对象类型,而非FactoryBean实现本身。

如果一定要取得FactoryBean本身的话,可以通过在bean定义的id之前加前缀&来达到目的。下面代码展示了获取FactoryBean本身与获取FactoryBean“生产”的对象之间的差别。

Object nextDayDate = container.getBean("nextDayDate"); 
assertTrue(nextDayDate instanceof DateTime); 

Object factoryBean = container.getBean("&nextDayDate"); 
assertTrue(factoryBean instanceof FactoryBean); 
assertTrue(factoryBean instanceof NextDayDateFactoryBean); 

Object factoryValue = ((FactoryBean)factoryBean).getObject(); 
assertTrue(factoryValue instanceof DateTime); 
assertNotSame(nextDayDate, factoryValue); 
assertEquals(((DateTime)nextDayDate).getDayOfYear(),((DateTime)factoryValue).getDayOfYear()); 

Spring容器内部许多地方了使用FactoryBean。下面是一些比较常见的FactoryBean实现,你可
以参照FactoryBean的Javadoc以了解更多内容。

  • JndiObjectFactoryBean
  • LocalSessionFactoryBean
  • SqlMapClientFactoryBean
  • ProxyFactoryBean
  • TransactionProxyFactoryBean
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值