定制Bean的性质
1.生命周期回调函数
可以通过实现Spring的InitializingBean与DisposableBean接口来与容器对bean生命周期的管理进行交互。
容器通过调用InitializingBean接口的afterPropertiesSet()方法在beans的initialization阶段执行特定的操作。
容器通过调用DisposableBean接口的destory()方法在beans的destruction阶段执行特定的操作。
JSR-250的 @PostConstruct、@PreDestroy注解通常被认为是在现在Spring应用中接收生命周期回调函数的最佳实践。
通过JSR的注解,可以使代码与Spring解耦和。
Spring框架内部使用BeanPostProcessor实现来运行其所能找到并调用的回掉接口中的合适方法。
如果需要定制Spring没有直接提供的特性或其他生命周期行为,可以实现一个自己的BeanPostProcessor。
除了initialization与destruction回调方法,Spring管理的对象还可以实现Lifecycle接口,使得这些对象可以如同被容器自身生命周期所驱动一般,参与startup与shutdown过程。
Initialization回调方法
void afterPropertiesSet() throws Exception;
note:此API在org.springframework.beans.factory.InitializingBean接口中定义。
note:此方法定义的初始化工作为在bean中所有必要属性已经被设置进入容器之后的初始化工作。
ps:由于与Spring耦合,并不推荐使用实现此接口的方法设置初始化行为。
ps:作为替代,可以使用@PostConstruct注解或基于xml配置指定一个初始化方法。
<bean id="example" class="examples.Example" init-method="init" />
public class Example {
void init() {
...
}
}
ps:在使用基于xml的配置元数据的情况下,通过使用init-method属性指定的一个void无参方法名称,令其作initialization回调方法。
Destruction回调方法
void destroy() throws Exception;
note:此API在org.springframework.beans.factory.DisposableBean接口中定义。
note:此方法为bean当持有它的容器被销毁时执行的回调。
ps:由于与Spring耦合,并不推荐使用此接口。
ps:作为替代,可以使用@PreDestroy注解或基于xml方法指定一个方法。
<bean id="example" class="examples.Example" destory-method="cleanup" />
package examples;
public class Example {
public void cleanup() {
...
}
}
默认initialization方法与destruction方法
<beans default-init-method="init">
<bean id="example" class="examples.Example" />
</beans>
public class Example {
public void init(){
}
}
ps:通过设置顶层元素<beans>的default-init-method属性,IoC容器可以识别出所有bean中名为init的方法,并将其作为初始化回调函数。
ps:可以通过使用<beans>元素中的default-destroy-method属性设置销毁回调函数。
ps:可以使用<bean>元素中的init-method属性与destroy-method重载默认回调函数。
当bean的所有依赖都被提供之后,配置的初始化回调函数会被立刻调用。
因此,回调函数将会被不完全bean的引用调用,也就是说AOP拦截器等不能被应用于此bean。
当一个bean被完整创建之后,拦截器链上的AOP代理才可以被应用。
如果一个bean与AOP代理分开定义,那么代码可以通过绕开代理直接与不完全bean交互。
因此,将拦截器应用于初始化方法将会引发不一致,因为当代码直接与不完全目标bean交互时,这样做会将目标bean的生命周期与它的代理与拦截器连接,并且遗留奇怪的语法。
结合生命周期机制
Spring2.5之后,存在三种控制bean生命周期行为的方法:
InitializaingBean、DisposableBean回调函数接口
定制init()、destroy()方法
@PostConstruct、@PreDestroy注解
相同bean的不同名初始化函数调用顺序:
@PostConstruct注解的方法
InitializaingBean回掉函数接口定义的afterPropertiesSet()
定制init()方法
相同bean的不同名销毁函数调用顺序:
@PreDestroy注解的方法
DisposableBean回掉函数接口定义的destroy()
定制destroy()方法
startup与shutdown回调函数
Lifecycle接口定义了任何一个拥有属于自身生命周期的对象的基础方法
public interface Lifecycle {
void start();
void stop();
boolean isRunning();
}
任何被Spring托管的对象都可以实现此接口。
当ApplicationContext自身接收start与stop信号,它将会级联调用定义在其中的Lifecycle接口实现。通过委托给LifecycleProcessor执行。
public interface LifecycleProcessor extends Lifecycle {
void onRefresh();
void onClose();
}
ps:LifecycleProcessor自身使Lifecycle的一个扩展,增添了对context刷新关闭时的反应方法。
ps:onClose()回调方法驱动shutdown过程就像显式调用stop()方法一样,但是它在context关闭时调用。
ps:当context被刷新时(所有对象已经实例化并且初始化之后),onRefresh()回调方法将会被调用,并且在这个时候,默认lifecycle processor会自动检测所有实现了SmartLifecycle接口的对象的isAutoStartup()返回的boolean值。
ps:如果返回值为true,这个对象将会被启动,而不是等待context的显示调用,或它自身的start()方法。(与context刷新不同,一个标准context实现不会自动发生context start。)
ps:phase就如同"depends-on"关系决定了startup的顺序。
Lifecycle接口是一个简单的显式约定,用于显式地start/stop通知并且在context刷新时,不会指示auto-startup。
stop通知并不保证在销毁之前到达。
通常在关闭时,在销毁回调接口被传递之前,所有的lifecycle beans将会首先接收一个stop通知。
然而,在热刷新与放弃刷新的context生命周期中,只有destroy方法会被调用。
启动与关闭调用的顺序有时比较重要。
当"depends-on"关系存在于两个对象之间时,依赖方将会在依赖启动之后启动,在依赖关闭之前关闭。
如果只知道某些类型的对象比其他类型的对象启动要优先,SmartLifecycle提供了定义在超接口中的getPhase()方法作为另一种选择。
public interface Phased {
int getPhase();
}
public interface SmartLifecycle extends Lifecycle, Phased {
boolean isAutoStartup();
void stop(Runnable callback);
}
ps:当开始时,最低阶段的对象将会首先开始,当结束时,上述顺序会反转。即,一个实现了SmartLifecycle接口并且其getPhase()方法返回Integer.MIN_VALUE的对象将会第一个开始,最后一个结束。
ps:一个没有实现Lifecycle接口的普通对象的phase为0。
ps:任何phrase值为负数的对象将会在标准组件之间开始,在其之后结束。
所有SmartLifecycle的实现都必须在实现的shutdown过程完成之后调用stop方法接受的回调接口的run方法。
这使得在有必要的地方进行异步shutdown,因为LifecycleProcessor的默认实现DefaultLifecycleProcessor为每一个阶段中对象组去调用回调接口等待设置的timeout值。
默认每个阶段的超时时间为30秒。
<bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor">
<!-- timeout value in milliseconds -->
<property name="timeoutPerShutdownPhase" value="10000"/>
</bean>
note:重载lifecycle processor设置的超时值。
非web应用中平缓关闭Spring IoC容器
基于web的Spirng应用的ApplicationContext实现已经包含了,当web应用关闭时,适当平缓关闭容器的代码。
如果在非web应用环境下,通过注册一个关联JVM关闭的钩子。这样做确保了平缓关闭以及调用单例bean上关联的销毁方法,以此确保所有的资源都被释放。
通过调用ConfigurableApplicationContext的resgisterShutDownHook()方法,注册关闭钩子。
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public final class Boot {
public static void main(final String[] args) throws Exception {
ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
// add a shutdown hook for the above context...
ctx.registerShutdownHook();
// app runs here...
// main method exits, hook is called prior to the app shutting down...
}
}
2.ApplicationContextAware 与 BeanNameAware
当ApplicationContext创建一个实现了org.springframework.context.ApplicationContextAware接口的对象时,这个对象实例会获得一个ApplicationContext的引用。
public interface ApplicationContextAware {
void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}
因此,beans可以以编程方式通过ApplicationContext接口操作ApplicationContext,或者是通过将引用转为一个已知的子类接口操作ApplicationContext,例如,转为ConfigurableApplicationContext。
一个常见的用法是检索使用其他beans。不过,不提倡使用这个接口,因为会提高代码与Spring框架的耦合程度,并且违反了控制反转。
Spring2.5之后,可以使用自动装配直接获得ApplicationContext,即通过constructor、byType自动装配模式。
当ApplicationContext创建一个实现了org.springframawork.beans.factory.BeanNameAware接口的对象时,该对象会获得一个对bean定义的名称的引用。
public interface BeanNameAware {
void setBeanName(String name) throws BeansException;
}
3.其他Aware接口
ps:Aware接口的使用会导致代码与Spring API的耦合。
ps:实现Aware的bean被推荐作为用于必需的编程访问容器的基础结构beans。
Bean定义继承
一个bean定义可以包含的信息有构造器参数、属性值、初始化方法、静态工厂方法名称等。
一个孩子bean定义可以继承双亲bean定义的配置数据。
孩子定义可以重写一些值或添加一些其他的必要值。
如果直接通过编程方式使用ApplicationContext接口,那么孩子bean定义使用ChildBeanDefinition类表示。
当使用基于xml的配置元数据时,通过指定孩子bean的parent属性,设置双亲bean。
<bean id="parentBean" class="examples.ParentBean"></bean>
<bean id="childBean" class="examples.ChildBean" parent="parentBean"></bean>
孩子bean定义中如果没有指定class,那么使用双亲bean定义的class。
孩子bean类需要与双亲兼容,即必须接受双亲属性的值。
孩子bean定义继承scope,构造器参数值,属性值以及添加可选新值的双亲方法的重载。
任何指定的scope、初始化方法、destory方法或静态工厂方法设置都会重载双亲设置。
总是会被继承的设置有:依赖、自动装配模式、依赖检测、单例、怠惰初始化。
如果双亲bean定义没有指定class,那么显示指定abstract属性是必要的。
<bean id="parentBean" abstract="true"></bean>
<bean id="childBean" class="examples.ChildBean" parent="parentBean"></bean>
当一个定义为abstract时,一般被用作专门被子bean定义继承的纯粹模板bean定义。
如果想要使用设置了abstract的bean,那么会返回一个错误。
容器内部preInstantiateSingletons()方法将会忽视abstract的bean定义。
Application默认会预初始化所有的单件。
如果想让设置了class的双亲bean不被预初始化,需要将其设置为abstract。