【0】REAMDE
0)本文部分文字描述转自:“how tomcat works”,旨在学习“tomat(16)关闭钩子”的相关知识;
1)problem+solution:
1.1)problem:在很多实际环境中,当用户关闭应用程序时,并不会按照推荐的方法关闭应用程序,很有可能不做清理工作;
1.2)solution:java 为程序员提供了一种优雅的方法可以在在关闭过程中执行一些代码,以确保那些负责善后处理的代码可能能够执行;
2)在java中,虚拟机会对两类事件(events)进行响应,然后执行关闭操作:
event1)当调用System.exit()方法或程序的最后一个非守护进程线程退出时,应用程序正常退出;
event2)用户突然强制虚拟机中断运行,例如用户按 CTRL+C 快捷键或在未关闭java程序的case下,从系统中退出;
3)虚拟机在执行关闭操作时,会经过以下两个阶段(stages):
stage1)虚拟机启动所有已经注册的关闭钩子,如果有的话。关闭钩子是先前已经通过 Runtime 类注册的线程,所有的关闭钩子会并发执行,直到完成任务;(干货——关闭钩子是线程)
stage2)虚拟机根据case 调用所有没有被调用过的 终结期(finalizer);
Attention)本文重点说明第一个stage,因为该阶段允许程序员告诉虚拟机在应用程序中执行一些清理代码。
4)创建关闭钩子很简单(steps):
step1)创建Thread类的一个子类 ;
step2)实现你自己的run()方法,当应用程序(正常或突然)关闭时,会调用此方法;
step3)在应用程序中,实例化关闭钩子类;
step4)使用当前 Runtime.addShutdownHook() 方法注册关闭钩子;
5)看个荔枝(关于 创建钩子的测试用例):
package com.tomcat.chapter16.shutdownhook;public class ShutdownHookDemo {public void start() {System.out.println("Demo");ShutdownHook shutdownHook = new ShutdownHook();Runtime.getRuntime().addShutdownHook(shutdownHook); // 添加一个关闭钩子.}public static void main(String[] args) {ShutdownHookDemo demo = new ShutdownHookDemo();demo.start();try {System.in.read();} catch (Exception e) {}System.out.println("Normal exit");}
}class ShutdownHook extends Thread {public void run() {System.out.println("Shutting down");}
}
对以上代码的分析(Analysis):
A1)在实例化ShutdownHookDemo 类后,main()方法会调用start()方法;start()方法会创建一个关闭钩子,并通过当前运行时注册它:
ShutdownHook shutdownHook = new ShutdownHook();Runtime.getRuntime().addShutdownHook(shutdownHook); // 添加一个关闭钩子.

A2)然后,应用程序会等待用户输入: System.in.read();

A3)当用户按 enter 键后,应用程序会退出。虚拟机会执行关闭钩子,输入字符串“Shutting down”;
【1】关闭钩子的荔枝
1)源代码如下(该应用程序在启动时会创建一个临时文件,并在关闭时删除该临时文件)
public class MySwingAppWithoutShutdownHook extends JFrame { //不带有关闭钩子的swingJButton exitButton = new JButton();JTextArea jTextArea1 = new JTextArea();String dir = System.getProperty("user.dir");String filename = "temp.txt";public MySwingApp() {exitButton.setText("Exit");exitButton.setBounds(new Rectangle(304, 248, 76, 37));exitButton.addActionListener(new java.awt.event.ActionListener() {public void actionPerformed(ActionEvent e) {exitButton_actionPerformed(e);}});this.getContentPane().setLayout(null);jTextArea1.setText("Click the Exit button to quit");jTextArea1.setBounds(new Rectangle(9, 7, 371, 235));this.getContentPane().add(exitButton, null);this.getContentPane().add(jTextArea1, null);this.setDefaultCloseOperation(EXIT_ON_CLOSE);this.setBounds(0, 0, 400, 330);this.setVisible(true);initialize();}private void initialize() {// create a temp fileFile file = new File(dir, filename);try {System.out.println("Creating temporary file");file.createNewFile();} catch (IOException e) {System.out.println("Failed creating temporary file.");}}private void shutdown() {// delete the temp fileFile file = new File(dir, filename);if (file.exists()) {System.out.println("Deleting temporary file.");file.delete();}}void exitButton_actionPerformed(ActionEvent e) {shutdown();System.exit(0);}public static void main(String[] args) {MySwingApp mySwingApp = new MySwingApp();}
}
对以上代码的分析(Analysis):
step1)应用程序会调用其 initialize()方法;
step2)initialize()方法会在用户目录中创建一个临时文件,名为 “temp.txt”;
step3)当用户关闭应用程序时,该程序会删除该临时文件;
2)problem+solution
2.1)problem:我们希望用户总是能够单击exit 来退出,这动作监听器就会调用shutdown()方法,可以删除临时文件了;但如果用户通过单击右上角的关闭按钮或是通过其他方式强制退出,则临时文件就无法删除了;
2.2)solution:使用关闭钩子来删除临时文件;关闭钩子是一个内部类,这样它就能够访问主类的所有方法;(干货——关闭钩子是一个内部类,这样它就能够访问主类的所有方法)
3)带有关闭钩子的源代码如下:
public class MySwingAppWithShutdownHook extends JFrame { //  带有关闭钩子的swing.JButton exitButton = new JButton();JTextArea jTextArea1 = new JTextArea();String dir = System.getProperty("user.dir");String filename = "temp.txt";public MySwingAppWithShutdownHook() {exitButton.setText("Exit");exitButton.setBounds(new Rectangle(304, 248, 76, 37));exitButton.addActionListener(new java.awt.event.ActionListener() {public void actionPerformed(ActionEvent e) {exitButton_actionPerformed(e);}});this.getContentPane().setLayout(null);jTextArea1.setText("Click the Exit button to quit");jTextArea1.setBounds(new Rectangle(9, 7, 371, 235));this.getContentPane().add(exitButton, null);this.getContentPane().add(jTextArea1, null);this.setDefaultCloseOperation(EXIT_ON_CLOSE);this.setBounds(0,0, 400, 330);this.setVisible(true);initialize();}private void initialize() {// add shutdown hookMyShutdownHook shutdownHook = new MyShutdownHook();Runtime.getRuntime().addShutdownHook(shutdownHook);// create a temp fileFile file = new File(dir, filename);try {System.out.println("Creating temporary file");file.createNewFile();}catch (IOException e) {System.out.println("Failed creating temporary file.");}}private void shutdown() { // highlight line.// delete the temp fileFile file = new File(dir, filename);if (file.exists()) {System.out.println("Deleting temporary file.");file.delete();}}void exitButton_actionPerformed(ActionEvent e) {shutdown();System.exit(0);}public static void main(String[] args) {MySwingAppWithShutdownHook mySwingApp = new MySwingAppWithShutdownHook();}private class MyShutdownHook extends Thread { // highlight line.public void run() {shutdown(); // highlight line.}}
}
对以上代码的分析(Analysis):
step1)程序会首先创建一个内部类MyShutdownHook的一个实例,即钩子类实例,并将注册该钩子实例;
step2)其他的代码与MySwingAppWithoutShutdownHook的源代码类似;
Attention)唯一不同的是,当突然关闭应用程序时,该临时文件总是会被删除;
【2】tomcat中的关闭钩子
1)intro:tomcat也是通过关闭钩子来完成退出过程的;
2)org.apache.catalina.startup.Catalina类:负责启动管理其他组件的Server对象。一个名为 CatalinaShutdownHook的内部类继承自 java.lang.Thread类,提供了run()方法的实现,它会调用 Server.stop()方法,执行关闭操作;

Supplement)本文习惯性地给出 Catalina.main()方法的调用过程:

protected void start() { //org.apache.catalina.startup.Catalina.start().//.....       // Replace System.out and System.err with a custom PrintStreamSystemLogHandler log = new SystemLogHandler(System.out);System.setOut(log);System.setErr(log);Thread shutdownHook = new CatalinaShutdownHook(); // 创建关闭钩子// Start the new serverif (server instanceof Lifecycle) {try {server.initialize();((Lifecycle) server).start();try {// Register shutdown hookRuntime.getRuntime().addShutdownHook(shutdownHook); // 添加关闭钩子.} catch (Throwable t) {// This will fail on JDK 1.2. Ignoring, as Tomcat can run// fine without the shutdown hook.}// Wait for the server to be told to shut downserver.await();} //......}// Shut down the serverif (server instanceof Lifecycle) {try {try {// Remove the ShutdownHook first so that server.stop() // doesn't get invoked twiceRuntime.getRuntime().removeShutdownHook(shutdownHook);} catch (Throwable t) {// This will fail on JDK 1.2. Ignoring, as Tomcat can run// fine without the shutdown hook.}((Lifecycle) server).stop();} //......}}
 protected class CatalinaShutdownHook extends Thread { //org.apache.catalina.startup.Catalina.CatalinaShutdownHook
// an inner class defined in Catalinapublic void run() {if (server != null) {try {((Lifecycle) server).stop(); // highlight line.} catch (LifecycleException e) {System.out.println("Catalina.stop: " + e);e.printStackTrace(System.out);if (e.getThrowable() != null) {System.out.println("----- Root Cause -----");e.getThrowable().printStackTrace(System.out);}}}              }}
Supplement)
S1)在Catalina实例启动时,会实例化关闭钩子,并在一个阶段将其添加到 Runtime类中;
S2)org.apache.catalina.startup.Catalina 类的源代码如下所示:(其中Catalina.createStartDigester() 创建了很多规则,规则集参见 tomcat(15)Digester库)
 public class Catalina { protected String configFile = "conf/server.xml";   protected boolean debug = false; protected ClassLoader parentClassLoader =ClassLoader.getSystemClassLoader();   protected Server server = null; protected boolean starting = false; protected boolean stopping = false; protected boolean useNaming = true;  public static void main(String args[]) {(new Catalina()).process(args);}public void process(String args[]) {setCatalinaHome();setCatalinaBase();try {if (arguments(args))execute();} catch (Exception e) {e.printStackTrace(System.out);}}public void setParentClassLoader(ClassLoader parentClassLoader) {this.parentClassLoader = parentClassLoader;}public void setServer(Server server) {this.server = server;} protected boolean arguments(String args[]) {boolean isConfig = false;if (args.length < 1) {usage();return (false);}for (int i = 0; i < args.length; i++) {if (isConfig) {configFile = args[i];isConfig = false;} else if (args[i].equals("-config")) {isConfig = true;} else if (args[i].equals("-debug")) {debug = true;} else if (args[i].equals("-nonaming")) {useNaming = false;} else if (args[i].equals("-help")) {usage();return (false);} else if (args[i].equals("start")) {starting = true;} else if (args[i].equals("stop")) {stopping = true;} else {usage();return (false);}}return (true);}protected File configFile() {File file = new File(configFile);if (!file.isAbsolute())file = new File(System.getProperty("catalina.base"), configFile);return (file);}protected Digester createStartDigester() {// Initialize the digesterDigester digester = new Digester();digester.setClassLoader(StandardServer.class.getClassLoader());if (debug)digester.setDebug(999);digester.setValidating(false);// Configure the actions we will be usingdigester.addObjectCreate("Server","org.apache.catalina.core.StandardServer","className");digester.addSetProperties("Server");digester.addSetNext("Server","setServer","org.apache.catalina.Server");digester.addObjectCreate("Server/GlobalNamingResources","org.apache.catalina.deploy.NamingResources");digester.addSetProperties("Server/GlobalNamingResources");digester.addSetNext("Server/GlobalNamingResources","setGlobalNamingResources","org.apache.catalina.deploy.NamingResources");digester.addObjectCreate("Server/Listener",null, // MUST be specified in the element"className");digester.addSetProperties("Server/Listener");digester.addSetNext("Server/Listener","addLifecycleListener","org.apache.catalina.LifecycleListener");digester.addObjectCreate("Server/Service","org.apache.catalina.core.StandardService","className");digester.addSetProperties("Server/Service");digester.addSetNext("Server/Service","addService","org.apache.catalina.Service");digester.addObjectCreate("Server/Service/Listener",null, // MUST be specified in the element"className");digester.addSetProperties("Server/Service/Listener");digester.addSetNext("Server/Service/Listener","addLifecycleListener","org.apache.catalina.LifecycleListener");digester.addObjectCreate("Server/Service/Connector","org.apache.catalina.connector.http.HttpConnector","className");digester.addSetProperties("Server/Service/Connector");digester.addSetNext("Server/Service/Connector","addConnector","org.apache.catalina.Connector");digester.addObjectCreate("Server/Service/Connector/Factory","org.apache.catalina.net.DefaultServerSocketFactory","className");digester.addSetProperties("Server/Service/Connector/Factory");digester.addSetNext("Server/Service/Connector/Factory","setFactory","org.apache.catalina.net.ServerSocketFactory");digester.addObjectCreate("Server/Service/Connector/Listener",null, // MUST be specified in the element"className");digester.addSetProperties("Server/Service/Connector/Listener");digester.addSetNext("Server/Service/Connector/Listener","addLifecycleListener","org.apache.catalina.LifecycleListener");// Add RuleSets for nested elementsdigester.addRuleSet(new NamingRuleSet("Server/GlobalNamingResources/"));digester.addRuleSet(new EngineRuleSet("Server/Service/"));digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Default"));digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/DefaultContext/"));digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/Default"));digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/DefaultContext/"));digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/Context/"));digester.addRule("Server/Service/Engine",new SetParentClassLoaderRule(digester,parentClassLoader));return (digester);}   protected Digester createStopDigester() {// Initialize the digesterDigester digester = new Digester();if (debug)digester.setDebug(999);// Configure the rules we need for shutting downdigester.addObjectCreate("Server","org.apache.catalina.core.StandardServer","className");digester.addSetProperties("Server");digester.addSetNext("Server","setServer","org.apache.catalina.Server");return (digester);}  protected void execute() throws Exception {if (starting)start();else if (stopping)stop();}protected void setCatalinaBase() {if (System.getProperty("catalina.base") != null)return;System.setProperty("catalina.base",System.getProperty("catalina.home"));}    protected void setCatalinaHome() {if (System.getProperty("catalina.home") != null)return;System.setProperty("catalina.home",System.getProperty("user.dir"));} protected void start() {// Setting additional variablesif (!useNaming) {System.setProperty("catalina.useNaming", "false");} else {System.setProperty("catalina.useNaming", "true");String value = "org.apache.naming";String oldValue =System.getProperty(javax.naming.Context.URL_PKG_PREFIXES);if (oldValue != null) {value = value + ":" + oldValue;}System.setProperty(javax.naming.Context.URL_PKG_PREFIXES, value);value = System.getProperty(javax.naming.Context.INITIAL_CONTEXT_FACTORY);if (value == null) {System.setProperty(javax.naming.Context.INITIAL_CONTEXT_FACTORY,"org.apache.naming.java.javaURLContextFactory");}}// Create and execute our DigesterDigester digester = createStartDigester();File file = configFile();try {InputSource is =new InputSource("file://" + file.getAbsolutePath());FileInputStream fis = new FileInputStream(file);is.setByteStream(fis);digester.push(this);digester.parse(is);fis.close();} catch (Exception e) {System.out.println("Catalina.start using "+ configFile() + ": " + e);e.printStackTrace(System.out);System.exit(1);}// If a SecurityManager is being used, set properties for// checkPackageAccess() and checkPackageDefinitionif( System.getSecurityManager() != null ) {String access = Security.getProperty("package.access");if( access != null && access.length() > 0 )access += ",";elseaccess = "sun.,";Security.setProperty("package.access",access + "org.apache.catalina.,org.apache.jasper.");String definition = Security.getProperty("package.definition");if( definition != null && definition.length() > 0 )definition += ",";elsedefinition = "sun.,";Security.setProperty("package.definition",// FIX ME package "javax." was removed to prevent HotSpot// fatal internal errorsdefinition + "java.,org.apache.catalina.,org.apache.jasper.,org.apache.coyote.");}// Replace System.out and System.err with a custom PrintStreamSystemLogHandler log = new SystemLogHandler(System.out);System.setOut(log);System.setErr(log);Thread shutdownHook = new CatalinaShutdownHook();// Start the new serverif (server instanceof Lifecycle) {try {server.initialize();((Lifecycle) server).start();try {// Register shutdown hookRuntime.getRuntime().addShutdownHook(shutdownHook);} catch (Throwable t) {// This will fail on JDK 1.2. Ignoring, as Tomcat can run// fine without the shutdown hook.}// Wait for the server to be told to shut downserver.await();} catch (LifecycleException e) {System.out.println("Catalina.start: " + e);e.printStackTrace(System.out);if (e.getThrowable() != null) {System.out.println("----- Root Cause -----");e.getThrowable().printStackTrace(System.out);}}}// Shut down the serverif (server instanceof Lifecycle) {try {try {// Remove the ShutdownHook first so that server.stop() // doesn't get invoked twiceRuntime.getRuntime().removeShutdownHook(shutdownHook);} catch (Throwable t) {// This will fail on JDK 1.2. Ignoring, as Tomcat can run// fine without the shutdown hook.}((Lifecycle) server).stop();} catch (LifecycleException e) {System.out.println("Catalina.stop: " + e);e.printStackTrace(System.out);if (e.getThrowable() != null) {System.out.println("----- Root Cause -----");e.getThrowable().printStackTrace(System.out);}}}}protected void stop() {// Create and execute our DigesterDigester digester = createStopDigester();File file = configFile();try {InputSource is =new InputSource("file://" + file.getAbsolutePath());FileInputStream fis = new FileInputStream(file);is.setByteStream(fis);digester.push(this);digester.parse(is);fis.close();} catch (Exception e) {System.out.println("Catalina.stop: " + e);e.printStackTrace(System.out);System.exit(1);}// Stop the existing servertry {Socket socket = new Socket("127.0.0.1", server.getPort());OutputStream stream = socket.getOutputStream();String shutdown = server.getShutdown();for (int i = 0; i < shutdown.length(); i++)stream.write(shutdown.charAt(i));stream.flush();stream.close();socket.close();} catch (IOException e) {System.out.println("Catalina.stop: " + e);e.printStackTrace(System.out);System.exit(1);}}    protected void usage() {System.out.println("usage: java org.apache.catalina.startup.Catalina"+ " [ -config {pathname} ] [ -debug ]"+ " [ -nonaming ] { start | stop }");}   protected class CatalinaShutdownHook extends Thread {public void run() {if (server != null) {try {((Lifecycle) server).stop();} catch (LifecycleException e) {System.out.println("Catalina.stop: " + e);e.printStackTrace(System.out);if (e.getThrowable() != null) {System.out.println("----- Root Cause -----");e.getThrowable().printStackTrace(System.out);}}}            }}
}
final class SetParentClassLoaderRule extends Rule {public SetParentClassLoaderRule(Digester digester,ClassLoader parentClassLoader) {super(digester);this.parentClassLoader = parentClassLoader;}ClassLoader parentClassLoader = null;public void begin(Attributes attributes) throws Exception {if (digester.getDebug() >= 1)digester.log("Setting parent class loader");Container top = (Container) digester.peek();top.setParentClassLoader(parentClassLoader);}
}

tomat(16)关闭钩子相关推荐

  1. JAVA虚拟机关闭钩子(Shutdown Hook)

    Java程序经常也会遇到进程挂掉的情况,一些状态没有正确的保存下来,这时候就需要在JVM关掉的时候执行一些清理现场的代码.JAVA中的ShutdownHook提供了比较好的方案. JDK提供了Java ...

  2. layui.open 关闭之后触发_JAVA虚拟机关闭钩子(Shutdown Hook)

    前言 当你认真的去看一个组件的源码的时候,你会经常看见这种关闭钩子的函数,如果你不了解的话,谷歌一下,你就会发现如下文章就是搜索引擎出来的第一篇,不愧是出自我们优秀的厮哒哒之笔. 正文 Java 程序 ...

  3. Java中的JVM关闭钩子

    java面试题网站:www.javaoffers.com Java中的JVM关闭钩子 (翻译篇)关机钩子 也叫 关闭钩子 关机钩子是一种特殊的构造,允许开发人员插入一段代码,以便在JVM关闭时执行.当 ...

  4. 关闭钩子(shutdown hool)

    Java提供了一种优雅的方式供程序员来使用,这样可以保证清理代码的执行.本章将会说明如何使用一个关闭钩子(shutdown hool)来保证清理代码一定会被执行. 在Java中,虚拟机遇到两种事件的时 ...

  5. 深入JVM关闭与关闭钩子

    1. 简述JVM关闭 通常而言,对于JVM的关闭我们很少去关注,但是了解JVM的关闭能帮我们在JVM关闭时做一些合理的事情.首先JVM的关闭方式可以分为三种: 正常关闭:当最后一个非守护线程结束或者调 ...

  6. nodemanager不能正常关闭_Java虚拟机关闭钩子(Shutdown hook)

    作者:俩右 出处:https://segmentfault.com/a/1190000038298447 源码点击 study:https://github.com/xiaoshuanglee/stu ...

  7. 关闭钩子(ShutdownHook)

    http://www.blogjava.net/zJun/archive/2006/03/12/34946.html 关闭钩子"(ShutdownHook)是这样一个概念:向虚拟机注册一个线 ...

  8. tomcat 正常关闭_Tomcat的带有守护程序和关闭钩子的正常关闭

    tomcat 正常关闭 我的最后两个博客讨论了长时间轮询和Spring的DeferredResult技术,并且为了展示这些概念,我将我的Producer Consumer项目中的代码添加到了Web应用 ...

  9. java 自定义 钩子_Java添加关闭钩子里面的方法

    在我的代码中,我使用CompletionService和ExecutorService来启动一堆Thread来执行某些任务(这可能需要很长时间) . 所以我有一个创建ExecutorService和C ...

最新文章

  1. 原生js写三级联动 java_原生js三级联动的简单实现代码
  2. 6-11数据库mysql初始
  3. python中class变量_对python 中class与变量的使用方法详解
  4. 全国计算机运用计算机绘图考试,计算机绘图期末考试题库
  5. Pytorch基础(九)——损失函数
  6. mvc的Controller返回值类型ActionResult详解
  7. EF(Entity FrameWork)实体框架
  8. 分布式唯一ID的几种生成方案
  9. mysql备份时过滤掉某些库 以及 去掉Warning: Using a password on the command line interface can be insecure.提示信息...
  10. 求计算机技术在创新上的应用,计算机技术在教学上的应用
  11. chrome http请求,测试webapp接口之DHC - REST/HTTP API Client
  12. 雷军立 Flag:小米 5 年 100 亿 All in AIoT
  13. 【软件测试】单元测试不属于动态测试
  14. C#类、方法的访问修饰符
  15. basename 从绝对路径中取得文件名
  16. 权限管理模块数据库设计
  17. 哪上班 | 好工作近在咫尺
  18. 基于EasyNVR实现RTSP/Onvif监控摄像头Web无插件化直播监控
  19. 快速高效入门3D建模学习教程,让你最快从小白到建模大师!
  20. CITA Release v0.18

热门文章

  1. 2020牛客多校第1场I-1 or 2一般图最大匹配带花树
  2. cf1555 E. Boring Segments
  3. [JLOI2015]战争调度
  4. 二分:[BJWC2008]秦腾与教学评估
  5. P3293-[SCOI2016]美味【主席树】
  6. P4300-[AHOI2006]上学路线【网络流,最短路】
  7. 【DP】剪草(jzoj 1510)
  8. 【dfs】简单游戏(jzoj 2121)
  9. P2053 SCOI2007 修车,费用流好题
  10. 数学推导题,NTT,快速数论变换,Wannafly-乒乓球