Tomcat启动入口就在脚本startup.sh中,具体脚本可以看tomcat的源码,这个启动脚本主要用来判断环境,
找到catalina.sh脚本路径,将启动参数传递给catalina.sh执行。
catalina.sh start 最终会执行org.apache.catalina.startup.Bootstrap中的main方法,并把start参数传入。
以后分析Tomcat关闭的时候,也是一个套路,最终都会调用到org.apache.catalina.startup.Bootstrap的main方法,并把stop参数传入。
分析main方法:
[Java] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
/**
       * 通过提供的脚本启动Tomcat时的主方法和入口点。
       *
       * @param args 要处理的命令行参数
       */
      public static void main(String args[]) {
 
          //main使用的守护进程对象。
          synchronized (daemonLock) {
              //daemon是volatile修饰的Bootstrap对象
              if (daemon == null) {
                  //在init()完成之前不要设置守护进程
                  Bootstrap bootstrap = new Bootstrap();
                  try {
                      //初始化守护进程。
                      bootstrap.init();
                  } catch (Throwable t) {
                      handleThrowable(t);
                      t.printStackTrace();
                      return;
                  }
                  daemon = bootstrap;
              } else {
                  // W当作为服务运行时,要停止的调用将位于新线程上,
                  // 因此请确保使用了正确的类加载器,以防止出现一系列类未找到异常。直到init()完成
                  Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
              }
          }
 
          try {
              String command = "start";
              if (args.length > 0) {
                  command = args[args.length - 1];
              }
 
              //命令解析与执行
              if (command.equals("startd")) {
                  args[args.length - 1] = "start";
                  daemon.load(args);
                  daemon.start();
              } else if (command.equals("stopd")) {
                  args[args.length - 1] = "stop";
                  daemon.stop();
              } else if (command.equals("start")) {
                  //启动操作
                  //通过反射调用守护进程引用的org.apache.catalina.startup.Catalina实例的setAwait方法
                  daemon.setAwait(true);
                  //调用Catalina实例的load方法
                  daemon.load(args);
                  //start方法
                  daemon.start();
                  //反射调用Catalina实例的getServer方法,返回的对象为空时,终止当前运行的Java虚拟机。
                  if (null == daemon.getServer()) {
                      System.exit(1);
                  }
              } else if (command.equals("stop")) {
                  //通过反射调用Catalina的stopServer方法。
                  daemon.stopServer(args);
              } else if (command.equals("configtest")) {
                  daemon.load(args);
                  if (null == daemon.getServer()) {
                      System.exit(1);
                  }
                  System.exit(0);
              } else {
                  log.warn("Bootstrap: command \"" + command + "\" does not exist.");
              }
          } catch (Throwable t) {
              // Unwrap the Exception for clearer error reporting
              if (t instanceof InvocationTargetException &&
                      t.getCause() != null) {
                  t = t.getCause();
              }
              handleThrowable(t);
              t.printStackTrace();
              System.exit(1);
          }
 
      }
启动过程有两步操作:
1、初始化守护进程,获取类加载器和反射实例化org.apache.catalina.startup.Catalina。
2、根据启动参数start,通过反射,依次执行Catalina实例的setAwait、load、start方法。
下面主要分析Catalina实例的setAwait、load、start方法:

1 setAwait

入参为true
[Java] 纯文本查看 复制代码
1
2
3
4
5
6
7
8
/**
   * Use await.
   */
   protected boolean await = false;
 
   public void setAwait(boolean b) {
       await = b;
   }

2 load

[Java] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
/**
        * 启动一个新的服务器实例。
        */
       public void load() {
           //防止重复加载。
           if (loaded) {
               return;
           }
           loaded = true;
           long t1 = System.nanoTime();
           //创建java.io.tmpdir文件夹
           initDirs();
 
           // Before digester - it may be needed
           //初始化jmx的环境变量
           initNaming();
 
           // Create and execute our Digester
           //创建和配置将用于启动的Digester。
           //配置解析server.xml中各个标签的解析类
           Digester digester = createStartDigester();
 
           InputSource inputSource = null;
           InputStream inputStream = null;
           File file = null;
           try {
 
               //下面一大段都是为了加载conf/server.xml配置文件,失败就加载server-embed.xml
               ...
               try {
                   inputSource.setByteStream(inputStream);
                   //把Catalina作为一个顶级容器
                   digester.push(this);
                   //解析过程会实例化各个组件,比如Server、Container、Connector等
                   digester.parse(inputSource);
               } catch (SAXParseException spe) {
                   log.warn("Catalina.start using " + getConfigFile() + ": " +
                           spe.getMessage());
                   return;
               } catch (Exception e) {
                   log.warn("Catalina.start using " + getConfigFile() + ": ", e);
                   return;
               }
           } finally {
               if (inputStream != null) {
                   try {
                       inputStream.close();
                   } catch (IOException e) {
                       // Ignore
                   }
               }
           }
 
           //这里的server在解析xml之后就有值了,这是Server的Catalina信息
           getServer().setCatalina(this);
           getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
           getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());
 
           // Stream redirection
           initStreams();
 
           // Start the new server
           try {
               //生命周期init方法
               getServer().init();
           } catch (LifecycleException e) {
               if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
                   throw new java.lang.Error(e);
               } else {
                   log.error("Catalina.start", e);
               }
           }
 
           long t2 = System.nanoTime();
           if (log.isInfoEnabled()) {
               log.info("Initialization processed in " + ((t2 - t1) / 1000000) + " ms");
           }
       }
主要流程:
  • 初始化JMX环境变量
  • 创建和配置Digester
  • 读取配置文件conf/server.xml配置文件,失败就加载server-embed.xml
  • 通过Digester解析配置文件,并将当前Catalina作为最顶层容器,解析过程会实例化各种组件
  • 设置Server组件的catalina信息
  • 调用Server组件的生命周期init方法
解析配置文件,注入catalina的各种组件后面分析。
下面看start方法:

3 start

[Java] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
/**
         * 启动一个新的服务器实例。
         */
        public void start() {
            //如果Server组件不存在,则重新执行load方法
            if (getServer() == null) {
                load();
            }
            //依然不存在就返回
            if (getServer() == null) {
                log.fatal("Cannot start server. Server instance is not configured.");
                return;
            }
            long t1 = System.nanoTime();
            // Start the new server
            try {
                //调用Server的start方法
                getServer().start();
            } catch (LifecycleException e) {
                log.fatal(sm.getString("catalina.serverStartFail"), e);
                try {
                    //异常的时候调用Server的destroy方法
                    getServer().destroy();
                } catch (LifecycleException e1) {
                    log.debug("destroy() failed for failed Server ", e1);
                }
                return;
            }
            long t2 = System.nanoTime();
            if (log.isInfoEnabled()) {
                log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");
            }
 
            // Register shutdown hook
            //注册关闭钩子
            if (useShutdownHook) {
                if (shutdownHook == null) {
                    // Catalina.this.stop();
                    shutdownHook = new CatalinaShutdownHook();
                }
 
                Runtime.getRuntime().addShutdownHook(shutdownHook);
 
                // If JULI is being used, disable JULI's shutdown hook since
                // shutdown hooks run in parallel and log messages may be lost
                // if JULI's hook completes before the CatalinaShutdownHook()
                LogManager logManager = LogManager.getLogManager();
                if (logManager instanceof ClassLoaderLogManager) {
                    ((ClassLoaderLogManager) logManager).setUseShutdownHook(
                            false);
                }
            }
            // Bootstrap中会设置await为true,其目的在于让tomcat在shutdown端口阻塞监听关闭命令
            if (await) {
                //等待收到正确的关机命令,然后返回。
                await();
                //停止现有的服务器实例。
                stop();
            }
        }
流程:
  • 调用Server组件的start方法,开启一个新的服务。
  • 注册关闭钩子
  • 让Tomcat在shutdown端口阻塞监听关闭命令
本篇目的就是了解整个Tomcat启动的主干流程,体现在代码层的就是依次执行Catalina实例的setAwait、load、start方法。
其中的load方法中的解析配置文件与注册组件、执行生命周期方法init;
start方法中的开启服务、注册关闭钩子、阻塞监听关闭指令等详细细节,将在后期慢慢分析。
更多技术资讯可关注:itheimaGZ获取

Tomcat源码解析-启动过程分析之主干流程的更多相关文章

  1. 渣渣菜鸡的 ElasticSearch 源码解析 —— 启动流程(下)

    关注我 转载请务必注明原创地址为:http://www.54tianzhisheng.cn/2018/08/12/es-code03/ 前提 上篇文章写完了 ES 流程启动的一部分,main 方法都入 ...

  2. 渣渣菜鸡的 ElasticSearch 源码解析 —— 启动流程(上)

    关注我 转载请务必注明原创地址为:http://www.54tianzhisheng.cn/2018/08/11/es-code02/ 前提 上篇文章写了 ElasticSearch 源码解析 -- ...

  3. Tomcat源码分析——启动与停止服务

    前言 熟悉Tomcat的工程师们,肯定都知道Tomcat是如何启动与停止的.对于startup.sh.startup.bat.shutdown.sh.shutdown.bat等脚本或者批处理命令,大家 ...

  4. Tomcat源码解析-整体流程介绍

    一.架构 下面谈谈我对Tomcat架构的理解 总体架构: 1.面向组件架构 2.基于JMX 3.事件侦听 1)面向组件架构 tomcat代码看似很庞大,但从结构上看却很清晰和简单,它主要由一堆组件组成 ...

  5. Okhttp3源码解析(3)-Call分析(整体流程)

    ### 前言 前面我们讲了 [Okhttp的基本用法](https://www.jianshu.com/p/8e404d9c160f) [Okhttp3源码解析(1)-OkHttpClient分析]( ...

  6. TOMCAT源码分析(启动框架)

    建议: 毕竟TOMCAT的框架还是比较复杂的, 单是从文字上理解, 是不那么容易掌握TOMCAT的框架的. 所以得实践.实践.再实践. 建议下载一份TOMCAT的源码, 调试通过, 然后单步跟踪其启动 ...

  7. 分布式事务_02_2PC框架raincat源码解析-启动过程

    一.前言 上一节已经将raincat demo工程运行起来了,这一节来分析下raincat启动过程的源码 主要包括: 事务协调者启动过程 事务参与者启动过程 二.协调者启动过程 主要就是在启动类中通过 ...

  8. 【Android】Android 4.0 Launcher2源码分析——启动过程分析

    Android的应用程序的入口定义在AndroidManifest.xml文件中可以找出:[html] <manifest xmlns:android="http://schemas. ...

  9. 【转】Android 4.0 Launcher2源码分析——启动过程分析

    Android的应用程序的入口定义在AndroidManifest.xml文件中可以找出:[html] <manifest xmlns:android="http://schemas. ...

随机推荐

  1. c语言:自增自减运算符的操作详解

    博主在回忆c语言的基本知识时,突然发现自增自减运算符(--.++)这个知识点有些模糊不清,故博主为了给同为小白的同学们提供一些经验,特写下这篇文章. 首先,自增自减运算符共有两种操作方式. 比如,我先 ...

  2. 关于 SQL Server 数据库批量备份与还原的一些 Tips

    一.前提 最近需要将服务器 A 上的数据库全部备份,并在服务器 B 上进行还原,30多个数据库一个一个地用鼠标点,先是 backup,之后时 restore……整个过程实在是太浪费时间了!于是直接写一 ...

  3. vim 马哥

    VIM编辑器 编辑模式   默认 输入模式   i 末行模式   : vim +# file   #打开文件后直接跳到第#行 vim + file   直接跳到尾行 vim  +/关键字    跳转到 ...

  4. canvas实现粒子星空连线

    <!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title>离 ...

  5. 基于迅为-i.MX6Q开发板制作镜像方法

    在“/home/imx6”目录(在前面编译 android 系统的时候新建过这个目录,如果没有可以自己新建一个)下,使用命令“mkdir minilinux/”新建 minilinux 目录,使用命令 ...

  6. Java 10按钮设计(awt)

    /** * 2019年8月9日08:03:41 * 目的:利用Java设计10个按钮 * @author 张涛 * */ //导入awt包 import java.awt.*; import java ...

  7. 进程间数据共享 (multiprocess.Manager)

    进程间数据共享 (multiprocess.Manager) 一.进程之间的数据共享 展望未来,基于消息传递的并发编程是大势所趋 即便是使用线程,推荐做法也是将程序设计为大量独立的线程集合,通过消息队 ...

  8. 10)global预定义变量

    代码展示: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w ...

  9. VS2010无法调试页面问题

    图片: VS2010报:未能将脚本调试器附加到计算机XXX上的进程iexplore.exe . 已附加了一个调试器”.启动调试失败. 解决:1.以管理员身份打开CMD; 2.运行:regsvr32.e ...

  10. php time()时间戳作为文件名产生文件同名的bug

    /*time()函数生成的文件名可能是相同的,因为如果php运行的过程如果足够快,time()函数调用的足够频繁,那么有可能time()生成的时间戳会相同,因为时间戳是以秒为单位,所以如果足够频繁有可 ...