又是一个以前没关注过的课题,发现学习Spring官方文档仍是有用的,一个个的知识点不断冒出来。html
意外的发现朱小厮https://blog.csdn.net/u013256816/ 大神也是CSDN重度患者,哈哈,向大神学习,好好写博客,应该有一天也能够出书的吧。java
闲话很少说了,先提出一个问题,什么叫作优雅关闭?git
咱们的java程序运行在JVM上,有不少状况可能会忽然崩溃掉,好比OOM、用户强制退出、业务其余报错。。。等一系列的问题可能致使咱们的进程挂掉。若是咱们的进程在运行一些很重要的内容,好比事务操做之类的,颇有可能致使事务的不一致性问题。因此,实现应用的优雅关闭仍是蛮重要的,起码咱们能够在关闭以前作一些记录补救操做。github
在java程序中,能够经过添加关闭钩子函数,实如今程序退出时关闭资源、平滑退出的功能。spring
如何作呢?app
主要就是经过Runtime.addShutDownHook(Thread hook)来实现的。下面咱们来简单看一个示例ide
// 建立HookTest,咱们经过main方法来模拟应用程序 public class HookTest { public static void main(String[] args) { // 添加hook thread,重写其run方法 Runtime.getRuntime().addShutdownHook(new Thread(){ @Override public void run() { System.out.println("this is hook demo..."); // TODO } }); int i = 0; // 这里会报错,咱们验证写是否会执行hook thread int j = 10/i; System.out.println("j" + j); } } // res Exception in thread "main" java.lang.ArithmeticException: / by zero at hook.HookTest.main(HookTest.java:23) this is hook demo... Process finished with exit code 1
总结:咱们主动写了一个报错程序,在程序报错以后,钩子函数仍是被执行了。经验证,咱们是能够经过对Runtime添加钩子函数来作到优雅停机。函数
既然JDK提供的这个方法能够注册一个JVM关闭的钩子函数,那么这个函数在什么状况下会被调用呢?上述咱们展现了在程序异常状况下会被调用,还有没有其余场景呢?单元测试
* 程序正常退出学习
* 使用System.exit()
* 终端使用Ctrl+C触发的中断
* 系统关闭
* OutofMemory宕机
* 使用Kill pid杀死进程(使用kill -9是不会被调用的)
1)Spring添加钩子函数比较简单,以下
// 经过这种方式来添加钩子函数 ApplicationContext.registerShutdownHook(); // 经过源码能够看到, @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); } } // 重点是这个doClose()方法 protected void doClose() { // Check whether an actual close attempt is necessary... if (this.active.get() && this.closed.compareAndSet(false, true)) { if (logger.isInfoEnabled()) { logger.info("Closing " + this); } LiveBeansView.unregisterApplicationContext(this); try { // Publish shutdown event. publishEvent(new ContextClosedEvent(this)); } catch (Throwable ex) { logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex); } // Stop all Lifecycle beans, to avoid delays during individual destruction. if (this.lifecycleProcessor != null) { try { this.lifecycleProcessor.onClose(); } catch (Throwable ex) { logger.warn("Exception thrown from LifecycleProcessor on context close", ex); } } // Destroy all cached singletons in the context's BeanFactory. destroyBeans(); // Close the state of this context itself. closeBeanFactory(); // Let subclasses do some final clean-up if they wish... onClose(); // Switch to inactive. this.active.set(false); } }
能够看到:doClose()方法会执行bean的destroy(),也会执行SmartLifeCycle的stop()方法,咱们就能够经过重写这些方法来实现对象的关闭,生命周期的管理,实现平滑shutdown
2)测试钩子
// 1.咱们以前生命周期管理的SmartLifeCycleDemo // 参考Spring容器生命周期管理:SmartLifecycle // 2.单元测试类,建立一个ApplicationContext @Test public void testXml(){ ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml"); // 注册钩子 applicationContext.registerShutdownHook(); } // 咱们能够对比下注册钩子与不注册的区别: // 1)不注册,只建立容器,结果以下: start // 只输出start,说明只执行了SmartLifeCycleDemo.start()方法 // 2)注册钩子 start stop(Runnable) // 当main方法执行结束时,主动执行了SmartLifeCycleDemo.stop()方法
总结:经过注册钩子函数,能够在程序中止前执行咱们自定义的各类destroy()或者stop()方法,用于优雅关闭。
注意:咱们能够对比上一篇关于SmartLifeCycle的文章中(https://blog.csdn.net/qq_26323323/article/details/89814304 )关于SmartLifeCycleDemo的测试,那个也是输出了stop,可是是由于主动调用了applicationContext.stop()方法因此才输出的,咱们当前并无主动调用stop()方法
咱们经过调用ApplicationContext.registerShutdownHook()来注册钩子函数,实现bean的destroy,Spring容器的关闭,经过实现这些方法来实现平滑关闭。
注意:咱们当前讨论的都是Spring非Web程序,若是是Web程序的话,不须要咱们来注册钩子函数,Spring的Web程序已经有了相关的代码实现优雅关闭了。
https://docs.spring.io/spring/docs/4.3.23.RELEASE/spring-framework-reference/htmlsingle/