从零开始的实习——Spring——BeanFactory(3)

bean的继承和抽象

假如某一天真的需要对FXNewsProvider使用继承进行拓展,那么可能会声明如下代码:

class SpecificFXNewsProvider extends FXNewsProvider
{
private IFXNewsListener newsListener;
private IFXNewsPersister newPersistener;
...
}

实际上,我们想让子类SpecificFXNewsProvider与FXNewsProvider使用相同的IFXNewsPersister,但IFXNewsListener不相同,那么可以使用如下的配置:

<bean id="superNewsProvider" class="..FXNewsProvider">
	<property name="newsListener">
		<ref bean="djNewsListener"/>
	</property>
	<property name="newPersistener">
		<ref bean="djNewsPersister"/>
	</property>
</bean>
<bean id="subNewsProvider" class="..SpecificFXNewsProvider">
	<property name="newsListener">
		<ref bean="specificNewsListener"/>
	</property>
	<property name="newPersistener">
		<ref bean="djNewsPersister"/>
	</property>
</bean>

我们可以看到,这种配置其实在<property name="newPersistener"><ref bean="djNewsPersister"/></property>上,是重复冗余的。所以,我们可以引入<bean>的继承机制:

<bean id="superNewsProvider" class="..FXNewsProvider">
	<property name="newsListener">
		<ref bean="djNewsListener"/>
	</property>
	<property name="newPersistener">
		<ref bean="djNewsPersister"/>
	</property>
</bean>
<bean id="subNewsProvider" parent="superNewsProvider" class="..SpecificFXNewsProvider">
	<property name="newsListener">
		<ref bean="specificNewsListener"/>
	</property>
</bean>

我们在声明subNewsProvider的时候,使用了parent属性,将其指定为superNewsProvider,这样就继承了父亲定义的默认值,只需要将有需要的属性修改,不需要重新定义一遍。
parent属性还可以与abstract属性结合使用,达到将相应bean定义模版化的目的:

<bean id="newsProviderTemplate" abstract="true">
	<property name="newPersistener">
		<ref bean="djNewsPersister"/> 
	</property>
</bean>
<bean id="superNewsProvider" parent="newsProviderTemplate" 
class="..FXNewsProvider">
	<property name="newsListener">
		<ref bean="djNewsListener"/>
	</property>
</bean>
<bean id="subNewsProvider" parent="newsProviderTemplate" 
class="..SpecificFXNewsProvider">
	<property name="newsListener">
		<ref bean="specificNewsListener"/> 
	</property> 
</bean>

使用了abstract属性表示该bean定义只是一个配置模版,不对应任何对象。
另外,容器在初始化对象实例的时候,不会关注abstract属性声明为true的bean的定义,这样就可以避免容器将其实例化。

对象的生命周期管理

BeanFactory除了拥有作为IoC Service Provider的职责,作为一个轻量级容器,他能对对象的生命周期进行管理。
scope用来声明容器中的对象的存活时间,当该对象不处于scope的限定后,容器通常会销毁这些对象。

  1. singleton
    单例,所有对该对象的引用将共享该实例。只要完成实例化后容器不销毁,那么该类型的bean就会一直存活。
<!-- DTD or XSD -->
<bean id="mockObject1" class="...MockBusinessObject"/>
<!-- DTD --> 
<bean id="mockObject1" class="...MockBusinessObject" singleton="true"/>
<!-- XSD -->
<bean id="mockObject1" class="...MockBusinessObject" scope="singleton"/>
  1. prototype
    容器在接到该类型对象的请求的时候,会每次都重新生成一个新的对象实例给请求方。容器一旦返回对象实例给请求方,容器就不再拥有当前返回对象的引用,所以需要请求方自行销毁(自生自灭)。
<!-- DTD -->
<bean id="mockObject1" class="...MockBusinessObject" singleton="false"/>
<!-- XSD -->
<bean id="mockObject1" class="...MockBusinessObject" scope="prototype"/>
  1. request/session和global session
    这三个scope实在Spring2.0之后新增的,它们不想之前的singleton和prototype那么通用。因为它们只适用于Web应用程序,通常与XmlWebApplicationContext共同使用(必须使用XSD的文档声明)。
    3.1 request
    为每个HTTP请求创建一个全新的RequestProcessor对象,当请求结束后,该对象实例的生命周期即告结束。相当于prototype的特例。
    3.2 session
    为每个独立的session创建属于它们自己的全新的UserPreferences对象实例。除了拥有session scope的bean的实例 比request scope的bean可能更长的存活时间,其它没什么区别。
    3.3 global session
    还是UserPreferences。只有在基于Portlet的web应用中才有意义。
    3.4 自定义scope类型
    略。

工厂方法和FactoryBean

假设有依赖于某一BarInterface接口的Foo类定义

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

如果该类是由我们设计并开发的,那么还好说,我们可以通过依赖注入,让容器帮助我们接触接口类和实现类之间的耦合性。但是有时候我们会依赖第三方库,需要实例化并使用第三方库的相关类,这时,接口与实现类的耦合性需要其他方式避免。
通常做法是使用工厂模式:

public class Foo
{
private BarInterface barInterface; 
public Foo()
{
	// barInterface = BarInterfaceFactory.getInstance(); 
	// 或者 
	// barInterface = new BarInterfaceFactory().getInstance();
}
 ...
}
  1. 静态工厂方法(Static Factory Method)
    假设某个第三方库发布了BarInterface,为了屏蔽对BarInterface实现类的变动,同时还提供了一个静态的工厂方法实现类StaticBarInterfaceFactory,代码如下:
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指定工厂方法名称,然后容器调用该静态方法工厂类的指定工厂方法(getInstance),并返回方法调用后的结果,即BarInterfaceImpl的实例。也就是说为foo注入的bar实际上是BarInterfaceImpl的实例,即方法调用后的结果,而不是静态工厂方法类。

某些时候,有的工厂类的工厂方法可能需要参数来返回相应实例:

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"/>
  1. 非静态工厂方法(Instance Factory Method)
    少了个static的getInstance方法:
public class NonStaticBarInterfaceFactory
{ 
	public BarInterface getInstance(){
		return new BarInterfaceImpl();
	} 
	...
}

因为是非静态的,所以需要如下配置:

<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"/>

如果有参数,加上<constructor-arg>即可。

  1. FactoryBean
    FactoryBean是Spring容器提供的一种可以拓展容器对象实例化逻辑的接口,请不要将其与BeanFactory想混淆。FactoryBean主要是Bean。
    要实现并使用自己的FactoryBean很简单,org.springframework.beans.factory.FactoryBean定义了三个方法:
public interface FactoryBean {
	Object getObject() throws Exception;//获得对象实例
	Class getObjectType();//返回对象类型
	boolean isSingleton();//是否是singleton
}

我们自己写的代码继承自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;
	}
}
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>

我们返回的是DateTime,它是FactoryBean生产的对象,而不是FactoryBean本身。
当然,要取得FactoryBean本身,我们可以通过在bean定义的id之前加前缀&即可:

Object nextDayDate = container.getBean("nextDayDate"); assertTrue(nextDayDate instanceof DateTime); 
Object factoryBean = container.getBean("&nextDayDate");
assertTrue(factoryBean instanceof FactoryBean);
assertTrue(factoryBean instanceof NextDayDateFactoryBean);
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值