Tomcat源码分析—1.tomcat入口

474 阅读2分钟

原文日期:2017-05-28


注:该系列文章如未特殊注明,默认使用的是官方tomcat8.0.38版本

tomcat是一个apache软件基金会下的核心项目,因其开源免费,是一款深受市场欢迎web服务器。没有特意去统计它的使用量,但工作四五年了,基本上都是和它打交道。为了做到知其然必先知其所以然,决定研究其源代码,剖析它的原理。

首先找到tomcat入口文件,我们知道启动tomcat是通过运行startup.sh/bat文件,停止服务是运行shutdown.sh/bat(sh是Linux操作系统下的运行的文件,bat是对应的是Windows操作系统),故而tomcat入口脚本文件就是startup.sh文件。我们进入startup.sh源文件,找到42行和60行,发现它调用了catalina.sh文件,如下是部分源代码。

PRGDIR=`dirname "$PRG"`
EXECUTABLE=catalina.sh   //调用了catalina.sh文件

# Check that target executable exists
if $os400; then
  # -x will Only work on the os400 if the files are:
  # 1. owned by the user
  # 2. owned by the PRIMARY group of the user
  # this will not work if the user belongs in secondary groups
  eval
else
  if [ ! -x "$PRGDIR"/"$EXECUTABLE" ]; then
    echo "Cannot find $PRGDIR/$EXECUTABLE"
    echo "The file is absent or does not have execute permission"
    echo "This file is needed to run this program"
    exit 1
  fi
fi

exec "$PRGDIR"/"$EXECUTABLE" start "$@"

接着顺藤摸瓜找到catalina.sh源文件。在这里能找到了启动程序的命令,从433行到439行,如下

    eval $_NOHUP "\"$_RUNJAVA\"" "\"$LOGGING_CONFIG\"" $LOGGING_MANAGER $JAVA_OPTS $CATALINA_OPTS \
      -Djava.endorsed.dirs="\"$JAVA_ENDORSED_DIRS\"" -classpath "\"$CLASSPATH\"" \
      -Dcatalina.base="\"$CATALINA_BASE\"" \
      -Dcatalina.home="\"$CATALINA_HOME\"" \
      -Djava.io.tmpdir="\"$CATALINA_TMPDIR\"" \
      org.apache.catalina.startup.Bootstrap "$@" start \
      >> "$CATALINA_OUT" 2>&1 "&"

从上面可以看出,tomcat启动的Java类是Bootstrap,即入口方法为Bootstrap.main()。接着我们进入这个方法,查看其代码。

    /**
     * Main method and entry point when starting Tomcat via the provided
     * scripts.
     *
     * @param args Command line arguments to be processed
     */
    public static void main(String args[]) {

        if (daemon == null) {
            // Don't set daemon until init() has completed
            Bootstrap bootstrap = new Bootstrap();
            try {
                bootstrap.init();
            } catch (Throwable t) {
                handleThrowable(t);
                t.printStackTrace();
                return;
            }
            daemon = bootstrap;
        } else {
            // When running as a service the call to stop will be on a new
            // thread so make sure the correct class loader is used to prevent
            // a range of class not found exceptions.
            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")) {
                daemon.setAwait(true);
                daemon.load(args);
                daemon.start();
            } else if (command.equals("stop")) {
                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);
        }

    }

从上代码主要做了如下三件事。

  • Bootstrap初始化init()。

  • Bootstrap加载服务load()。

  • Boostrap启动start()/停止stop()/退出exit()。

总结

通过shell启动文件的指令,可以找到Tomcat启动的Java方法入口,其实也可以通过查看进程信息做到的。

$ ps -ef| grep tomcat

Tomcat启动主要做了以下几个事情。

  • Bootstrap初始化init()。

  • Bootstrap加载服务load()。

  • Boostrap启动start()/停止stop()/退出exit()。