(二) Catalina类

  运行Bootstrap启动类后,会创建Catalina实例,并调用其process()方法。

org.apache.catalina.startup.Catalina类

  先看process方法做了那些事情

public void process(String args[]) {

    setCatalinaHome();  //设置"catalina.home"路径
    setCatalinaBase();  //设置"catalina.base"路径
    try {
        if (arguments(args))    //分析命令带入的参数
            execute();      //执行
    } catch (Exception e) {
        e.printStackTrace(System.out);
    }
}
protected void execute() throws Exception {
    if (starting)   //若启动参数带有start,则starting变量为true
        start();
    else if (stopping)  //若启动参数带有stop,则stopping变量为true
        stop();

}

  每个启动参数-config -debug -nonaming start stop其实都会对应一个变量,记录这些启动参数,控制程序运行时的部分细节.

Catalina类中的start()
protected void start() {

        // Create and execute our Digester
        /**
         * 加载分析/conf/server.xml的digester
         */
        Digester digester = createStartDigester();

        /**
         * 找到server.xml文件
         * 解析server.xml文件 组装成想要的server对象并将其赋值给当前catalina类
         */
        File file = configFile();
        try {
            InputSource is =
                new InputSource("file://" + file.getAbsolutePath());
            FileInputStream fis = new FileInputStream(file);
            is.setByteStream(fis);
            digester.push(this);        //--------这一步解释了digester规则中-匹配Server的规则-digester.addSetNext("Server"...)
            digester.parse(is);
            fis.close();
        } catch (Exception e) {
            System.out.println("Catalina.start: " + e);
            e.printStackTrace(System.out);
            System.exit(1);
        }


        // Setting additional variables
        if (!useNaming) {
            System.setProperty("catalina.useNaming", "false");
        } else {
            System.setProperty("catalina.useNaming", "true");
            //TODO 
            //....有待分析
        }

        // If a SecurityManager is being used, set properties for
        // checkPackageAccess() and checkPackageDefinition
        if( System.getSecurityManager() != null ) {
            String access = Security.getProperty("package.access");
            //TODO 
            //....有待分析
        }

        // 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);
                }
            }
        }

    }

  这个是Tomcat启动的关键,包含了以下流程:

  1. 创建了server.xml文件的digerster(用于解析xml文件并依据制定的规则创建对应的类);
  2. 找到并解析server.xml文件,完成后,catalina类中的变量server就是依照server.xml配置生成的对象;
  3. useNaming…(待分析);
  4. securityManager…(待分析);
  5. 配置关闭钩子–CatalinaShutdownHook();
  6. server.initialize(); ((Lifecycle) server).start();这两步,包含了启动所有的Tomcat要使用的组件(一个包含connectors和container的service)
  7. server.wait();监听另外的端口的停止命令,无线循环直至收到停止命令;
  8. 收到命令后才会继续执行的:移除钩子(避免执行两次停止程序),执行server.stop();,关闭server服务器组件包含的所有内容;
    备注:server表示Catalina的整个Servlet引擎,囊括所有的组件。有了它,我们就无需再对connectors和container(处理http请求的两大关键模块 )分别启动/关闭了
Catalina类中的stop()
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);
        }
    }

  server的关闭流程:

  1. 依据server.xml创建关闭所需的digester(比启动简单的多),解析server.xml;
  2. 向server侦听的关闭端口(解析server.xml文件获取)发送关闭指令,通过start()流程中的步骤8,关闭server.

  分析可以看出,Catalina中的server是一个很重要的对象,整个流程基本可概括为构建server对象,启动server以及关闭server,server是干什么的呢。

问题
  1. catalina也有main函数, 为什么要建一个Bootstrap用反射的机制来调用catalina.process()
    public static void main(String args[]) {
        (new Catalina()).process(args);
    }
    
    这样启动和调用Bootstrap类启动有什么区别?
    答: Bootstrap类开头的一段注释解释了为什么
    * Bootstrap loader for Catalina.  This application constructs a class loader
    * for use in loading the Catalina internal classes (by accumulating all of the
    * JAR files found in the "server" directory under "catalina.home"), and
    * starts the regular execution of the container.  The purpose of this
    * roundabout approach is to keep the Catalina internal classes (and any
    * other classes they depend on, such as an XML parser) out of the system
    * class path and therefore not visible to application level classes.
    
    这种迂回的方式的目的就是将catalina的内部类以及其他catalina所依赖的类与应用层的calsspath区分开,这样的话就能保证catalina所使用依赖的类对应用程序级别的类不可见.
延伸
论钩子的作用和工作原理

github上的示例代码
  Runtime.getRuntime().addShutdownHook(shutdownHook);在jvm中增加一个关闭的钩子,当jvm关闭的时候,会执行系统中已经设置的所有通过方法addShutdownHook添加的钩子,当系统执行完这些钩子后,jvm才会关闭。所以这些钩子可以在jvm关闭的时候进行内存清理、对象销毁等操作.

什么是Runtime

  通过Runtime实例,使得应用程序和其运行环境相连接。Runtime是在应用启动期间自动建立,应用程序不能够创建Runtime,但可以通过Runtime.getRuntime()来获得当前应用的Runtime对象引用,通过该引用我们可以获得当前运行环境的相关信息,比如空闲内存、最大内存以及为当前虚拟机添加关闭钩子(addShutdownHook()),执行指定命令(exec())等等.

jvm关闭

jvm的关闭方式有三种:

  1. 正常关闭:当最后一个非守护线程结束或者调用了System.exit或者通过其他特定平台的方法关闭(发送SIGINT,SIGTERM信号等);
  2. 强制关闭:通过调用Runtime.halt方法或者是在操作系统中直接kill(发送SIGKILL信号)掉JVM进程;
  3. 异常关闭:运行中遇到RuntimeException异常等。
关闭钩子(shutdown hooks)

  一些情况下,我们需要在jvm关闭时做一些扫尾清理工作。为此JVM提供了关闭钩子(shutdown hooks)来做这些事情。另外特别注意的是:如果JVM因异常关闭,那么子线程(Hook本质上也是子线程)将不会停止。但在JVM被强行关闭时,这些线程都会被强行结束.
  当应用程序遇到非正常关闭时,我们在应用程序中加的钩子就能起作用了,帮我们进行清理工作,Tomcat就是利用钩子在程序异常退出时正常的让server stop。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值