初始化回调
实现org.springframework.beans.factory.InitializingBean接口,afterPropertiesSet方法将在bean的所有属性装配好之后调用
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements InitializingBean {
public void afterPropertiesSet() {
// do some initialization work
}
}
自定义初始化方法,指定init-method
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
public class ExampleBean {
public void init() {
// do some initialization work
}
}
析构回调
只针对scope=单例,对于prototype的bean没有以下销毁方法,但对于其他的Scope指定的Bean,由scope对象的registerDestructionCallback注册回调
实现org.springframework.beans.factory.DisposableBean接口,destroy将在容器销毁时调用
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements DisposableBean {
public void destroy() {
// do some destruction work (like releasing pooled connections)
}
}
自定义销毁方法,指定destroy-method
<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>
public class ExampleBean {
public void cleanup() {
// do some destruction work (like releasing pooled connections)
}
}
析构回调只对单例scope=singleton有效。以上几个方法的调用顺序为
- afterPropertiesSet()
- 自定义init()
- destroy()
- 自定义destroy()
容器刷新和关闭回调
Lifecycle接口为有自己生命周期的(比如管理一组线程)组件对象提供了必要的方法,Phased接口定义了组件执行的先后顺序,返回值越小,开始执行的越早,结束执行的越晚
SmartLifecycle接口除了继承以上两个接口的特点,还定义了两个方法isAutoStartup和
stop(Runnable callback)。
容器初始化时,会触发所有的Lifecycle对象的start方法。对于SmartLifecycle对象,而且会在容器的refresh事件触发时调用start,只要isAutoStartup返回true。
stop(Runnable)支持异步式关闭操作。
public interface Lifecycle {
void start();
void stop();
boolean isRunning();
}
public interface Phased {
int getPhase();
}
public interface SmartLifecycle extends Lifecycle, Phased {
boolean isAutoStartup();
void stop(Runnable callback);
}
lifecycle处理流程
- LifecycleProcessor在容器初始化或refresh事情触发,或者作
为其他 LifecycleProcessor调度对象时执行start调度,结束时执行stop调度
- 所有lifecycle对象的phrase值=0,和smartLifecycle对象一起被调度;
- 所有phrase值相同的lifecycle或其子对象被放置到一个组中,这样组之间通过phrase
由小到大排序先后被调度
- 最终,发现lifecycle之间的依赖关系,存在管理A->B,B实例作为A的属性,B作
为lifecyle 对象被调度之前,先检查A,如果A也为lifecyle且没有被调度过,则
先被 调度;这样直到依赖B的所有lifecycle对象调度完,才执行B的调度
- 组内的所有成员执行完STOP之前,主线程会一直诸塞,除非
指定的timeoutPerShutdownPhase时间到,使用JDK CountDownLatch实现。这种
机制就好比上课铃响了,如果班级的学生没有到齐,那在教室的所有学生必须等待!
---一个也不能少;或者加班时把任务分成N份给每个人,先完成的必须等待当所有人
任务结束之后才能聚餐!不过我们可以指定最长等待时间正如timeoutPerShutdownPhase
- timeoutPerShutdownPhase默认为30s,可需重新指定或者使用自定义lifecycleProcessor,bean的标识一定是"lifecycleProcessor"配置如下
<bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor">
<!-- timeout value in milliseconds -->
<property name="timeoutPerShutdownPhase" value="10000"/>
</bean>
在非web环境下优雅的关闭IOC容器
在web环境下,IOC容器在装载时已经注入了“钩子”,这些“钩子”是一些线程。当JVM关闭时,这些“钩子”会开始运行,这里我们可以调用单例对象的销毁方法,当然必须正确定义这些销毁方法
在非WEB环境下如测试环境中,必须手动调用registerShutdownHook()注册"钩子".
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public final class Boot {
public static void main(final String[] args) throws Exception {
AbstractApplicationContext ctx
= new ClassPathXmlApplicationContext(new String []{"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...
}
}
“钩子”的注册落实到runtime.addShutdownHook(Thread thread).
JDK解释
关闭钩子 只是一个已初始化但尚未启动的线程。虚拟机开始启用其关闭序列时,它会以某种未指定的顺序启动所有已注册的关闭钩子,并让它们同时运行。运行完所有的钩子后,如果已启用退出终结,那么虚拟机接着会运行所有未调用的终结方法。最后,虚拟机会暂停。注意,关闭序列期间会继续运行守护线程,如果通过调用 exit 方法来发起关闭序列,那么也会继续运行非守护线程。
启动钩子调用->如果设置runFinalizersOnExit(true)启用退出时终结,则强制调用所有对象的仍未调用的finalize()方法(此方法在对象不可用时调用,强制性调用可能会对没有回收的对象即被其他对象引用,调用将是不安全的)
package com.tmmh.th;
public class Main {
/**
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
Runtime.runFinalizersOnExit(true);
Man man = new Man();
// man = null;
Runtime.getRuntime().addShutdownHook(new Thread(){
@Override
public void run() {
System.out.println("hook call");
}
});
//System.gc();
//Runtime.getRuntime().runFinalization();
}
}
package com.tmmh.th;
public class Man {
@Override
protected void finalize() throws Throwable {
// TODO Auto-generated method stub
System.out.println("finalize call");
}
}
hook call
finalize call
halt方法调用将强行终止JVM,钩子和对象的finalize()从不被调用,即使已经在运行中,也会终止。
一旦开始了关闭序列,则不可能注册新的关闭钩子或取消注册先前已注册的钩子。尝试执行这些操作会导致抛出 IllegalStateException。
关闭钩子可在虚拟机生命周期中的特定时间运行,因此应保护性地对其进行编码。特别是应将关闭钩子编写为线程安全的,并尽可能地避免死锁。关闭钩子还应该不盲目地依靠某些服务,这些服务可能已注册了自己的关闭钩子,所以其本身可能正处于关闭进程中。例如,试图使用其他基于线程的服务(如 AWT 事件指派线程)可能导致死锁。
关闭钩子应该快速地完成其工作。当程序调用 exit 时,虚拟机应该迅速地关闭并退出。由于用户注销或系统关闭而终止虚拟机时,底层的操作系统可能只允许在固定的时间内关闭并退出。因此在关闭钩子中尝试进行任何用户交互或执行长时间的计算都是不明智的。
与其他所有线程一样,通过调用线程 ThreadGroup 对象的 uncaughtException 方法, 可在关闭钩子中处理未捕获的异常。此方法的默认实现是将该异常的堆栈跟踪打印至 System.err 并终止线程;它不会导致虚拟机退出或暂停
Aware接口
当ApplicationContext创建实现org.springframework.context.ApplicationContextAware接口的对象时,这个对象被提供ApplicationContext的引用,通过可以进行硬编码与容器交互。注入的时机在装配属性之后,afterPropertiesSet 之前。
bean创建时的顺序
- postProcessBeforeInstantiation返回可能的代理对象如自动代理,如果有,则调用postProcessAfterInitialization方法,创建结束。
- 通过实例化策略:工厂方法、构造装配、简单实例化创建对象
- postProcessMergedBeanDefinition通过元数据信息注入到beanDefinition
- 添加防止循环依赖的ObjectFactory
- postProcessAfterInstantiation返回false,则停止装配属性
- postProcessPropertyValues修改属性值,返回null则停止装配属性
- 根据依赖策略检查要设置的属性,抛出依赖检查异常终止bean获取装配属性
- 依次调用BeanNameAware、BeanClassLoaderAware、BeanFactoryAware设置方法
- postProcessBeforeInitialization方法,例如ApplicationContextAwareProcessor中调用EnvironmentAware、EmbeddedValueResolverAware、ResourceLoaderAware、ApplicationEventPublisherAware、MessageSourceAware、ApplicationContextAware设置方法
- afterPropertiesSet
- 自定义init-method
- postProcessAfterInitialization,比如自动代理
- 循环依赖检查:如果防止循环依赖的ObjectFactory已经创建,说明确实有过循环依赖。如果初始化后的bean和之前指定的是同一对象,则返回防止循环依赖的ObjectFactory已经创建的bean;如果不是同一对象,比如初始化时创建了代理;对于这种情况作如下检查
allowRawInjectionDespiteWrapping为false(默认为false)
有依赖bean的对象,且这些对象时不作为类型检查时而创建的对象
以上两种情况都存在,则抛出循环依赖异常
- 对于单例对象,注册DisposableBean;对于自定义scope对象,通过scope.registerDestructionCallback注册DisposableBean