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的限定后,容器通常会销毁这些对象。
- 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"/>
- prototype
容器在接到该类型对象的请求的时候,会每次都重新生成一个新的对象实例给请求方。容器一旦返回对象实例给请求方,容器就不再拥有当前返回对象的引用,所以需要请求方自行销毁(自生自灭)。
<!-- DTD -->
<bean id="mockObject1" class="...MockBusinessObject" singleton="false"/>
<!-- XSD -->
<bean id="mockObject1" class="...MockBusinessObject" scope="prototype"/>
- 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();
}
...
}
- 静态工厂方法(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"/>
- 非静态工厂方法(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>
即可。
- 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);