Tomcat 9 源码分析(1)— 启动与停止

Tomcat 9 源码分析(1)— 启动与停止

前言

本文分析的Tomcat版本为Tomcat9.0,该版本与Tomcat8,Tomcat7大体一致,仅在部分地方有所改动,而目前最新的Tomcat10则与之前的版本相差较大。

这是本人第一次阅读主流技术的源码,碍于个人能力原因,无法独自完成源码的阅读,因此本文参考了许多大佬们的文章,在本次学习中主要参考了泰山不老生博主的博客,很感谢能够翻阅到大佬的文章,所学甚多,受益匪浅。

愿编码半生,如一生老友,你也加油。

Tomcat模块

在这里插入图片描述

Tomcat的模块如图,接下来便从Tomcat的启动开始分析

启动

Tomcat的启动往往是调用tomcat/bin目录下的startup脚本,而startup.sh将会调用catalina.sh脚本

tomcat/bin/startup.sh

PRGDIR=`dirname "$PRG"`
EXECUTABLE=catalina.sh

if $os400; then
  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 "$@"

主要变量:

  • PRGDIR:当前shell脚本所在的路径
  • EXECUTABLE:脚本catalina.sh

startup.sh内最后会调用catalina.sh,初始化catalina_base,catalina_home等参数,并传递参数start,catalina.sh脚本则初传递各种参数调用Bootstrap.jar执行其main方法且传递传输start

tomcat/bin/catalina.sh

elif [ "$1" = "start" ] ; then
    
  #省略参数校验脚本
  
  eval $_NOHUP "\"$_RUNJAVA\"" "\"$CATALINA_LOGGING_CONFIG\"" $LOGGING_MANAGER "$JAVA_OPTS" "$CATALINA_OPTS" \
  -D$ENDORSED_PROP="\"$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 "&"

由此可知,最后使用java命令执行了org.apache.catalina.startup.Bootstrap类中的main方法,参数是start,在Bootstrap.main()中,当传递参数start时变量command等于start

Bootstarp.main()内先调用init()初始化,利用反射机制,正式载入Catalina类,创建其实例返回并赋值给Bootstrap类的catalinaDaemon对象

org.apche.catalina.startup.Bootstrap.main()

public static void main(String args[]) {
   

    synchronized (daemonLock) {
   
        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 {
   
            Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
        }
    }
    // 省略后续操作,在后文分析
   
}

init()

org.apache.catalina.startup.Bootstrap.init()

public void init() throws Exception {
   
    //  初始化catalina.properties中的commonLoader,serverLoader,sharedLoader
    //  如果catalina.properties中的server.loader和sharedLoader为空则以common.Loader替代
    //  查找各个loader中的系统变量,其中包含了以","分隔的文件路径(URL、JAR、目录等),称之为repository
    //  调用ClassLoaderFactory.createClassLoader()产生classLoader
    initClassLoaders();

    //  把生成的catalinaLoader设置为当前线程上下文的classloader
    Thread.currentThread().setContextClassLoader(catalinaLoader);

    SecurityClassLoad.securityClassLoad(catalinaLoader);

    // Load our startup class and call its process() method
    if (log.isDebugEnabled())
        log.debug("Loading startup class");
    //  利用反射机制,正式载入Catalina类,创建其实例返回
    Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
    Object startupInstance = startupClass.getConstructor().newInstance();

    //  Set the shared extensions class loader
    //  调用setParentClassLoader()将sharedLoader设置为父类加载器
    //  将catalinaLoader实例赋值给Bootstrap类的catalinaDaemon对象
    if (log.isDebugEnabled())
        log.debug("Setting startup class properties");
    String methodName = "setParentClassLoader";
    Class<?> paramTypes[] = new Class[1];
    paramTypes[0] = Class.forName("java.lang.ClassLoader");
    Object paramValues[] = new Object[1];
    paramValues[0] = sharedLoader;
    Method method =
        startupInstance.getClass().getMethod(methodName, paramTypes);
    method.invoke(startupInstance, paramValues);

    catalinaDaemon = startupInstance;
}

initClassLoaders()将初始化catalina.properties中的commonLoader,serverLoader,sharedLoader,且以commonLoader为父类加载器

org.apache.catalina.startup.Bootstrap.initClassLoaders()

private void initClassLoaders() {
   
    try {
   
        commonLoader = createClassLoader("common", null);
        if (commonLoader == null) {
   
            // no config file, default to this loader - we might be in a 'single' env.
            commonLoader = this.getClass().getClassLoader();
        }
        catalinaLoader = createClassLoader("server", commonLoader);
        sharedLoader = createClassLoader("shared", commonLoader);
    } catch (Throwable t) {
   
        handleThrowable(t);
        log.error("Class loader creation threw exception", t);
        System.exit(1);
    }
}
//common.loader:tomcat自身和各web服务都会用到的类
//server.loader:tomcat自身会用到的类 如果为空则由common.loader替代
//shared.loader:各web服务会用到的类 如果为空则由common.loader替代
common.loader="${catalina.base}/lib","${catalina.base}/lib/*.jar","${catalina.home}/lib","${catalina.home}/lib/*.jar"
server.loader=
shared.loader=

这三个类加载器都是UrlClassLoader

org.apache.catalina.startup.ClassLoaderFactory.createClassLoader()

return AccessController.doPrivileged(
        (PrivilegedAction<URLClassLoader>) () -> {
   
            if (parent == null)
                return new URLClassLoader(array);
            else
                return new URLClassLoader(array, parent);
        });

至此org.apache.catalina.startup.Bootstrap.init()方法执行完毕,接下来main方法继续执行,在后续代码中根据catalina.sh最后的%ACTION%参数进行不同的操作,而main方法内的setAwait、load、start均为使用反射调用catalinaDaemon(即Catalina类)对应的方法

org.apache.catalina.startup.Bootstrap.main()

public static void main(String args[]) {
   
	// 省略初始化代码,前文已分析

    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();
            if (null == daemon.getServer()) {
   
                System.exit(1);
            }
        } 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);
    }
}

setAwait()

当启动tomcat时传递的参数command为start,此时首先会调用Bootstrap.setAwait(),在setAwait方法内会使用反射调用Catalina类中的setAwait方法设置启动flag

org.apache.catalina.startup.Catalina.setAwait()

public void setAwait(boolean await)
    throws Exception {
   

    Class<?> paramTypes[] = new Class[1];
    paramTypes[0] = Boolean.TYPE;
    Object paramValues[] = new Object[1];
    paramValues[0] = Boolean.valueOf(await);
    Method method =
        catalinaDaemon.getClass().getMethod("setAwait", paramTypes);
    method.invoke(catalinaDaemon, paramValues);
}

load()

在执行完setWait后便调用load正式初始化载入

org.apache.catalina.startup.Catalina.load()

public void load() {
   
    if (loaded) {
   
        return;
    }
    loaded = true;
    long t1 = System.nanoTime();
    initDirs();
    // Before digester - it may be needed
    initNaming();
    //  Parse main server.xml
    //  处理server.xml并初始化实例化部分模块
    parseServerXml(true);
    Server s = getServer();
    if (s == null) {
   
        return;
    }
    getServer().setCatalina(this);
    getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
    getServer()
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值