由于工程需要,需要起一个CT任务。在CT任务的时候,起的是一个main方法。在结束的时候调用了System.exit(0)。但是发现调用System.exit的时候,无论是finally还是Spring的@PreDestroy注解的方法都没有执行。所以去查了相应的资料。
How does Java's System.exit() work with try/catch/finally blocks
可见System.exit是直接调用了关闭Jvm的方法,自然不会调用Spring的BeanFactory的关闭方法。
我们通过源码可以更加直观的观察到其运行方法:
public static void exit(int status) {
Runtime.getRuntime().exit(status);
}
从方法的注释中可以看出此方法是结束当前正在运行的Java虚拟机,这个status表示退出的状态码,非零表示异常终止。注意:不管status为何值程序都会退出,和return 相比有不同的是:return是回到上一层,而System.exit(status)是回到最上层。
由以上分析可知,在使用System.exit的时候并不会调用finally中的方法,更别提Spring的注解@PreDestroy。
如果希望上面的情况能够正常工作,需要如下的进行:
获取ApplicationContext。在AbstractApplicationContext中有一个方法方法close()可以调用。所以可以如下进行:
public static void shutDown() {
LOGGER.info("start to shut down application context");
if (null != ac) {
if (ac instanceof AbstractApplicationContext) {
AbstractApplicationContext beanFactory = (AbstractApplicationContext) ac;
beanFactory.close();
}
}
}
同时将System.exit(0)删除即可。
如同上面讲的,除了采用调用AbstractApplicationContext的close()方法外,还也可以使用
((AbstractApplicationContext)appCtx).registerShutdownHook();
讨论参考how-to-close-a-spring-applicationcontext
来将applicationContext注册到jvm关闭的钩子上,这样在jvm关闭的时候就会自动调用AbstractApplicationContext的doClose方法。
这部分的内容可以参考源码来进一步了解:
@Override
public void registerShutdownHook() {
if (this.shutdownHook == null) {
// No shutdown hook registered yet.
this.shutdownHook = new Thread() {
@Override
public void run() {
synchronized (startupShutdownMonitor) {
doClose();
}
}
};
Runtime.getRuntime().addShutdownHook(this.shutdownHook);
}
}
最本质的内容就是调用了Runtime.getRuntime().addShutdownHook(Thread hook);