标签:
1.1)problem:在很多实际环境中,当用户关闭应用程序时,并不会按照推荐的方法关闭应用程序,很有可能不做清理工作;1.2)solution:java 为程序员提供了一种优雅的方法可以在在关闭过程中执行一些代码,以确保那些负责善后处理的代码可能能够执行;
event1)当调用System.exit()方法或程序的最后一个非守护进程线程退出时,应用程序正常退出;event2)用户突然强制虚拟机中断运行,例如用户按 CTRL+C 快捷键或在未关闭java程序的case下,从系统中退出;
stage1)虚拟机启动所有已经注册的关闭钩子,如果有的话。关闭钩子是先前已经通过 Runtime 类注册的线程,所有的关闭钩子会并发执行,直到完成任务;(干货——关闭钩子是线程)stage2)虚拟机根据case 调用所有没有被调用过的 终结期(finalizer);
step1)创建Thread类的一个子类 ;step2)实现你自己的run()方法,当应用程序(正常或突然)关闭时,会调用此方法;step3)在应用程序中,实例化关闭钩子类;step4)使用当前 Runtime.addShutdownHook() 方法注册关闭钩子;
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"); } }
ShutdownHook shutdownHook = new ShutdownHook(); Runtime.getRuntime().addShutdownHook(shutdownHook); // 添加一个关闭钩子.
public class MySwingAppWithoutShutdownHook extends JFrame { //不带有关闭钩子的swing JButton 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 file File 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 file File 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(); } }
step1)应用程序会调用其 initialize()方法;step2)initialize()方法会在用户目录中创建一个临时文件,名为 “temp.txt”;step3)当用户关闭应用程序时,该程序会删除该临时文件;
2.1)problem:我们希望用户总是能够单击exit 来退出,这动作监听器就会调用shutdown()方法,可以删除临时文件了;但如果用户通过单击右上角的关闭按钮或是通过其他方式强制退出,则临时文件就无法删除了;2.2)solution:使用关闭钩子来删除临时文件;关闭钩子是一个内部类,这样它就能够访问主类的所有方法;(干货——关闭钩子是一个内部类,这样它就能够访问主类的所有方法)
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 hook MyShutdownHook shutdownHook = new MyShutdownHook(); Runtime.getRuntime().addShutdownHook(shutdownHook); // create a temp file File 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 file File 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. } } }
step1)程序会首先创建一个内部类MyShutdownHook的一个实例,即钩子类实例,并将注册该钩子实例;step2)其他的代码与MySwingAppWithoutShutdownHook的源代码类似;
protected void start() { //org.apache.catalina.startup.Catalina.start(). //..... // Replace System.out and System.err with a custom PrintStream SystemLogHandler log = new SystemLogHandler(System.out); System.setOut(log); System.setErr(log); Thread shutdownHook = new CatalinaShutdownHook(); // 创建关闭钩子 // Start the new server if (server instanceof Lifecycle) { try { server.initialize(); ((Lifecycle) server).start(); try { // Register shutdown hook Runtime.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 down server.await(); } //...... } // Shut down the server if (server instanceof Lifecycle) { try { try { // Remove the ShutdownHook first so that server.stop() // doesn't get invoked twice Runtime.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 Catalina public 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); } } } } }
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 digester Digester digester = new Digester(); digester.setClassLoader(StandardServer.class.getClassLoader()); if (debug) digester.setDebug(999); digester.setValidating(false); // Configure the actions we will be using digester.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 elements digester.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 digester Digester digester = new Digester(); if (debug) digester.setDebug(999); // Configure the rules we need for shutting down digester.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 variables if (!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 Digester Digester 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 checkPackageDefinition if( System.getSecurityManager() != null ) { String access = Security.getProperty("package.access"); if( access != null && access.length() > 0 ) access += ","; else access = "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 += ","; else definition = "sun.,"; Security.setProperty("package.definition", // FIX ME package "javax." was removed to prevent HotSpot // fatal internal errors definition + "java.,org.apache.catalina.,org.apache.jasper.,org.apache.coyote."); } // Replace System.out and System.err with a custom PrintStream SystemLogHandler log = new SystemLogHandler(System.out); System.setOut(log); System.setErr(log); Thread shutdownHook = new CatalinaShutdownHook(); // Start the new server if (server instanceof Lifecycle) { try { server.initialize(); ((Lifecycle) server).start(); try { // Register shutdown hook Runtime.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 down server.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 server if (server instanceof Lifecycle) { try { try { // Remove the ShutdownHook first so that server.stop() // doesn't get invoked twice Runtime.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 Digester Digester 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 server try { 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); } }
标签:
原文地址:http://blog.csdn.net/pacosonswjtu/article/details/51506556