容器内的bean能够设置一些回调方法,以执行bean自身初始化&销毁,或响应容器的某些事件。
InitializingBean接口就一个方法afterPropertiesSet(),如果bean实现了该接口,当容器完成对bean的创建和依赖注入之后,会调用这个方法。Bean可以在此完成一些初始化工作。
类似地,DisposableBean包含destory()方法,如果bean实现了该接口,当容器销毁这个bean的时候,会先调用这个方法。不过要注意,前面一章已经说过,容器并不对所有bean的生命周期负责,ProtoType作用域的bean是接受不到该回调的。
通过JSR-250包含的java标准注解,可以使代码与spring框架解耦,使用方式如下:
public class ExampleBean { @PostContruct public void init() { // do some initialization work } @PreDestroy public void destroy() { // do some cleanup work } }可以在bean的定义中指定初始化或销毁回调,如下:
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init" destroy-method="cleanup"/>其中init方法等价与的第一方式的InitializingBean.afterPropertiesSet;cleanup方法等价于DisposableBean.destroy。
如果你的系统对init和destroy方法有统一的命名规范,那么可以这样统一指定:
<beans default-init-method="init"> //bean的定义 </beans>那么,只要bean有名字匹配的init()方法,就会被当做初始化回调;当然具体bean的定义中也可以覆盖默认行为。
一个bean可以同时使用上面三种机制 这些回调全部发生在容器完成对bean的属性注入之后,它们的相对顺序如下: 1、@PostConstruct注解的方法; 2、afterPropertiesSet()方法; 3、bean配置指定的init方法
销毁回调的顺序如下: 1、@PreDestroy注解的方法; 2、DisposableBean接口的destroy()方法; 3、bean配置指定的destroy方法;
Spring支持多种初始化&销毁方式,部分出于历史原因,在同一个项目里面,我们尽量使用同一种方式,更不要依赖上面规定的调用顺序。
bean如果想接受容器的start或stop信号,以执行或停止某些任务,可以实现Lifecycle接口。
public interface Lifecycle { void start(); void stop(); boolean isRunning(); } isRunning是一个查询接口,返回bean背后的任务是否在运行;当容器start的时候,如果isRunning返回false,bean的start方法会被调用,容器关闭的时候,如果isRunning返回true,stop方法会被调用。如果一个bean依赖于另一个bean,那么后者的start的方法会先被调用。不过这种顺序规则一方面比较模糊,另一方面bean之间的依赖可能是间接的,为了解决这个问题,可以使用SmartLifecycle。
SmartLifecycle的定义如下:
public interface SmartLifecycle extends Lifecycle, Phased { boolean isAutoStartup(); void stop(Runnable callback); } public interface Phased { int getPhase(); }getPhase方法返回值规定了相对顺序,返回的值越小,start的顺序越靠前,stop的顺序越靠后。如果bean没有实现SmartLifecycle,而是实现了Lifecycle,可以认为phase值是0。
SmartLifecycle定义了一个额外的stop(Runnable callback)方法,bean在stop完成后通过callback通知容器,这样可以实现异步且限时的stop方(容器只等待bean stop方法执行有限时间)。这个时间限制默认是30秒,可以修改:
<bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor"> <!-- timeout value in milliseconds --> <property name="timeoutPerShutdownPhase" value="10000"/> </bean>SmartLifecycle的isAutoStartup()方法提供了另一个机制,如果bean的isAutoStartup返回true,那么当ApplicationContext被刷新(Refeshed,所有的bean对象完成初始化),bean的start方法会被调用,而不需要等待显示的ApplictionContext.start()调用。
context并不会主动触发start信号,需要我们手动调用context.start();容器关闭的时候会触发stop信号,我们也可以手动调用context.stop()来触发。但是context的start和stop并不影响容器的状态,也不会影响bean的状态,仅仅按规则触发LifeCycle回调而已。
注:本人从来没用过这个机制,也没想到什么依赖此机制的场景。
容器关闭的时候,也会触发stop信号;这引发另一个问题,当进程结束时,容器能够正常关闭吗。对于非web的ApplicationContext,如果要优雅地关闭容器,需要我们调用context的registerShutdownHook()方法;对于web应用,web容器保证了优雅关闭。
实例代码里的contextStartAndStop模块展示了context的start和stop。
Spring提供了一些接口,使得客户代码可以获取诸如context等容器内部对象。
ApplicationContextAware接口让bean可以获得创建它的context的引用,接口的定义如下:
public interface ApplicationContextAware { void setApplicationContext(ApplicationContext applicationContext) throws BeansException; }bean只要实现这个接口,就可以通过这个回调拿到ApplicationContext实例并保存起来备用,最常见的用途是通过context动态地获取其他bean实例或加载资源。
Spring2.5之后,可以通过Autowire机制来获得Context实例,如下:
public class ExampleBean { //通过构造参数autowire public ExampleBean(ApplicationContext context) { this.context = context } //或者通过属性autowire @Autowire private ApplicationContext context; }BeanNameAware可以让bean知道自己在容器里面的名字:
public interface BeanNameAware { void setBeanName(String name) throws BeansException; }这个回调发生在bean的依赖注入之后,上一节讲的那些init方法之前。
Spring提供了很多类似的接口,可以让用户代码访问Spring内部功能:
Name注入的依赖ApplicationContextAwareApplicationContextApplicationEventPublisherAware容器内事件的PublisherBeanClassLoaderAwarebean的类加载器BeanFactoryAwareBean工厂BeanNameAwarebean的名字BootstrapContextAware在JCA-ware AplictionContext里面,外部环境上下文LoadTimeWeaverAwareAspectJ相关,暂不讨论MessageSourceAware文本国际化相关NotificationPublisherAwareSpring JMX 通知publisherResourceLoaderAware底层资源加载器ServletConfigAware容器运行的ServeletConfig,web应用有效ServletContextAware容器运行的ServletContext,web应用有效使用这些接口,会让你的代码与Spring框架紧耦合,建议仅在有必要时使用,且封装在少数类里面。