自Spring2.5,可以通过三种方式控制Bean的生命周期回调方法:
通过初始化回调接口InitializingBean和销毁回调接口DisposableBean。 public class MyBean implements InitializingBean, DisposableBean { //... public void afterPropertiesSet() throws Exception { } public void destroy() throws Exception { } } 通过XML配置指定自定义回调方法。 <!-- init-method:指定初始化回调方法名 destroy-method:指定销毁回调方法名 --> <bean id="myBean" class="org.xxx.MyBean" init-method="init" destroy-methosd="destroy" /> 通过JSR-250规范注解@PostConstruct和@PreDestroy指定回调方法。(推荐) @PostConstruct public void init() throws Exception { } @PreDestroy public void destroy() throws Exception { }我们通过在同一Bean中使用三种方式,可以了解到它们的优先级顺序:注解式 > 接口式 > XML式。
为减少不必要的耦合,推荐使用XML配置或注解配置方式。
此外,我们还可以通过设置默认回调方法名称来全局指定该XML配置文件下的所有Bean回调方法名称。
<beans default-init-method="init" default-destroy-method="destory" > ... </beans>对于Bean来说,生命周期还得结合作用域来讨论,这里通过常用的单例和多例来简单描述一下。
单例Bean:
创建:容器初始化完成时,开始创建销毁:容器销毁,进行销毁多例Bean:
创建:需要使用该类时,开始创建销毁:当对象长时间不用时,对象由Java回收机制进行销毁通过生命周期机制,对于在生命周期有业务需求(如执行任务或启动服务)的任何Spring管理的对象,Spring提供了定义生命周期需求的基本方法的接口Lifecycle。
public interface Lifecycle { //初始化后回调,如果已运行,不会抛出异常 void start(); //停止时回调 void stop(); //判断是否正在运行。 //只有返回false时,start方法才会被执;只有返回true时,stop方法才会被执行。 boolean isRunning(); }当容器上下文本身收到start或stop信号时,它会将信号传播至整个容器组件,即所有组件调用相应的方法,通过委派其子接口LifecycleProcessor实现,它实现了容器上下文生命周期过程更多的操作。
public interface LifecycleProcessor extends Lifecycle { //容器刷新时调用 void onRefresh(); //容器关闭时调用 void onClose(); }然而,以上两种接口只会在容器上下文显式调用start()、stop()时,才会调用其组件的相应方法,并不会在容器上下文刷新时自启。
为此,对于需要有能自动回调生命周期方法的需求,Spring还提供了SmartLifecycle 接口,该接口继承Lifecycle和Phased接口。
public interface SmartLifecycle extends Lifecycle, Phased { //默认相位值,值越小,启动优先级越高 int DEFAULT_PHASE = Integer.MAX_VALUE; //如果需容器上下文刷新时自动回调生命周期方法,则返回true,反之为false //默认为true default boolean isAutoStartup() { return true; } default void stop(Runnable callback) { stop(); callback.run(); } //返回相位值 @Override default int getPhase() { return DEFAULT_PHASE; } } public interface Phased { int getPhase(); }两个Bean之间有依赖关系,则依赖项在依赖端开始之前开始、停止之后停止。但有一种情况,若它们并无直接的依赖关系,但又要求有启动顺序(如depends-on),对于这种情况,则由Phased接口实现,通过其指定PHASE值来确定生命周期启动顺序,请注意,并不会影响实例化顺序。
PHASE值最低的对象最先开始,最后停止,即开始和停止是相反的顺序执行。对于未实现SmartLifecycle接口的Bean来说,其PHASE值为0,因此,PHASE值若为负,则表示这些对象在标准组件之前开始,且在它们之后停止。
相较于其他两个接口,SmartLifecycle接口的stop()支持接收回调方法,表示该对象关闭后调用此回调方法run(),且该回调的默认超时值为30s,若需修改,通过注册DefaultLifecycleProcessor并修改其属性值即可。
<bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor"> <property name="timeoutPerShutdownPhase" value="20000"/> </bean>下面进行演示:
首先,定义两个Bean,内容大致一致。Bean1的PHASE值为2,Bean2的PHASE值为1。
@Component public class Bean1 implements SmartLifecycle { private int PHASE = 2; private boolean isRunning = false; public Bean1() { System.out.println("bean1"); } public void start() { isRunning = true; System.out.println("start:bean1"); } public void stop() { isRunning = false; System.out.println("stop:bean1"); } public boolean isRunning() { return isRunning; } //开启自启 public boolean isAutoStartup() { return true; } public void stop(Runnable callback) { stop(); //执行run()才表示stop()方法结束,不使用这步操作表示该方法未结束,直到超时。 callback.run(); } public int getPhase() { return PHASE; } } @Component public class Bean2 implements SmartLifecycle { private int PHASE = 1; private boolean isRunning = false; public Bean1() { System.out.println("bean2"); } //... }然后写配置类。
@Configuration @ComponentScan({"org.xxx.bean"}) public class MyConfig { @Bean("lifecycleProcessor") public DefaultLifecycleProcessor defaultLifecycleProcessor(){ DefaultLifecycleProcessor dp = new DefaultLifecycleProcessor(); //设置超时时长为5s dp.setTimeoutPerShutdownPhase(5000); return dp; } }主入口,打印输出结果体现PHASE仅影响生命周期启动顺序,不影响实例化顺序。
public class Main { public static void main(String[] args) { AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(); ac.register(MyConfig.class); //通过刷新自启生命周期回调,无需显式发送信号 ac.refresh(); ac.close(); } } /**打印输出: bean1 bean2 start:bean2 start:bean1 stop:bean1 stop:bean2 */DefaultLifecycleProcessor当容器上下文被刷新时,会自动检查每个SmartLifecycle接口实现类种中isAutoStartup()返回布尔值,若为true,则启动该对象的生命周期回调,而不是等待容器上下文的显式启动信号。
Spring IOC容器支持通过特殊接口实现容器的扩展。
通过注册一或多个实现BeanPostProcessor接口的Bean使其成为Bean后置处理器,达到对容器中Bean实例的定制(自定义实例化逻辑等)。
BeanPostProcessor接口拥有两个回调方法,作用是在Bean初始化方法前后进行回调。当容器注册了Bean后置处理器,会对该容器中的每个Bean都起作用(不包括其他容器的Bean)。
注意:初始化不等于实例化,初始化指在Bean实例化并且属性注入之后进行。
Bean后置处理器可以与其他Bean以相同方式注册在容器中,同时容器会自动检测带有BeanPostProcessor接口的Bean作为Bean后置处理器。
//@Order(2) 可以对其排序 public class MyBeanPostProcessor implements BeanPostProcessor { public Object postProcessBeforeInitialization(Object bean, String beanName) { return bean; } public Object postProcessAfterInitialization(Object bean, String beanName) { System.out.println("Bean '" + beanName + "' created : " + bean.toString()); return bean; } } <!-- 在容器中注册 --> <bean class="scripting.InstantiationTracingBeanPostProcessor"/>由于AOP自动代理是通过BeanPostProcessor自身实现的,所以BeanPostProcessor实例还是及其实现类都不适合进行自动代理。
你也可以通过@Order注解或者实现Ordered接口对多个Bean后置处理器进行排序,来决定它们的执行顺序。
通过注册一或多个实现BeanFactoryPostProcessor接口的Bean使其成为BeanFactory后置处理器,与Bean后置处理器类似,但有一个主要的区别:它主要用于对Bean元数据进行操作,也就是Bean实例化之前的操作。如果想要更改的是实际的Bean实例,则使用BeanPostProcessor接口。
BeanFactory后置处理器会在容器声明Bean工厂时自动调用。(Spring包含了许多预定义的BeanFactory后置处理器,如PropertyOverrideConfigurer或PropertySourcesPlaceholderConfigurer)
BeanFactory后置处理器虽然可以实现过早的Bean实例化,但这将违背生命周期机制,可能导致负面的作用,甚至影响Bean的后期其他处理,请谨慎使用!
下面通过PropertyOverrideConfigurer的预定义BeanFactory后置处理器进行演示,该后置处理器实现将外部文件定义到Bean元数据中(如数据库信息)。
<!-- 两种注册方式: 1.bean注册方式 --> <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"> <property name="locations" value="classpath:com/something/jdbc.properties"/> </bean> <!-- 2.context注册方式 <context:property-placeholder location="classpath:com/something/jdbc.properties"/> --> <!-- 被处理的Bean --> <bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean>如果你有复杂的初始化过程的Bean,可以选择通过实现FactoryBean接口进行工厂实例化Bean,它提供三个方法:
Object getObject():获取实例boolean isSingleton():是否为单例Class getObjectType():获取对象类型Spring中FactoryBean接口被多次使用,此接口拥有十几种实现类或接口,你可以根据需要选择。
此外,如果你在请求容器获取FactoryBean实例的时候,需要的是FactoryBean实例本身而不是所生成的Bean,你可以在其限定符之前加“&”即可,这也是FactoryBean是特殊的工厂Bean原因之一。
ObjectFactory接口跟FactoryBean接口类似,但只提供一个方法getObject()。它们的区别在于:前者更看重的是获取Bean的普通工厂实现,后者更看重的是Bean的初始化过程。