SpringShutdowHook与RocketMq等中使用的Hook不一样,它不是传统意义上的钩子函数接口,而是一个线程,这个线程被注册在RunTime类中,RunTime再把它注册到ApplicationShutdownHooks类中,在调用Shutdown.exit和Shutdown.shutdown时,会启动ShutdownApplicationShutdownHooks中的所有Hook,并把他们join到当前线程中。
Shutdown.exit与Shutdown.shutdown的调用时机:
exit:程序主动调用System.exit或因为异常退出时,调用此方法,它在方法末尾会发送一个halt命令告诉JVM强制关闭。
shutdown:程序正常结束,最后一个非守护线程也被销毁后,调用此方法,它不会发送任何命令,只是做一些常规的回收。
在最后程序结束时,所有Hook线程都是并发运行的。
SpringShutdownHook在Spring生命容器中的生命周期:
该Hook在ApplicationContext加载完所有的Bean,所有的postProcessor都被执行完毕,并且执行完毕所有的postConstruct,ApplicationContextRefreshEvent事件已经发布后,该Hook被注册到Shutdown中。
SpringShutdownHook的执行过程就是执行ApplicationContext.doClose()方法
protected void doClose() {
// Check whether an actual close attempt is necessary...
if (this.active.get() && this.closed.compareAndSet(false, true)) {
if (logger.isDebugEnabled()) {
logger.debug("Closing " + this);
}
LiveBeansView.unregisterApplicationContext(this);
try {
// 发布ContextClosedEvent
publishEvent(new ContextClosedEvent(this));
}
catch (Throwable ex) {
logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);
}
//主动调用所有实现了Lifecycle的Bean对象的stop方法.
if (this.lifecycleProcessor != null) {
try {
this.lifecycleProcessor.onClose();
}
catch (Throwable ex) {
logger.warn("Exception thrown from LifecycleProcessor on context close", ex);
}
}
// 销毁所有单例对象.
destroyBeans();
// 销毁BeanFactory.
closeBeanFactory();
// 子类实现,默认什么也不做.
onClose();
// 将所有监听器删除,只保留Spring默认的监听器.
if (this.earlyApplicationListeners != null) {
this.applicationListeners.clear();
this.applicationListeners.addAll(this.earlyApplicationListeners);
}
// Switch to inactive.
this.active.set(false);
}
}
模仿Spring的ShutdownHook,自己实现一个ShutdownHook:
public static void main(String[] args) {
//localhost:9081/....
SpringApplication.run(ApplicationStarter.class,args);
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override
public void run() {
System.out.println("我也监听到了关闭事件");
}
}));
Thread thread = new Thread(()->{
try {
Thread.sleep(10*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.exit(1);
});
thread.start();
}