Tomat启动-源码跟踪

《看透SpringMVC源码分析与实践》
Tomcat源码分析—-初始化与启动

tomcat环境搭建

源码下载
由于项目使用的tomcat版本时7.47,从apache svn check代码,svn地址是:
http://svn.apache.org/repos/asf/tomcat/tc7.0.x/tags/TOMCAT_7_0_47/

idea环境搭建
idea搭建tomcat7开发环境,参照基于IntelliJ IDEA 15.0.2的Tomcat7.0.69源码运行环境搭建

  • 将tomcat_7_0_47转换为Maven工程,添加 pom.xml 文件。
  • 在tomcat_7_0_47的源码根目录下,新建 catalina-base目录,作为Tomcat的工作目录
  • 将 conf、logs(新建目录)、webapps、work (新建目录)文件夹,移入 catalina-base目录
  • idea引入import Project –>pom.xml
  • 删除无用的文件、文件夹(非强迫症患者,此步可跳过)。删除bin/,modules/,res/,test/,build.properties等文件,最终的项目结构如下图:
    这里写图片描述
  • 运行org.apache.catalina.startup.Bootstrap.main(),jvm参数为:-Dcatalina.base="你的工程目录\catalina-base",log配置为output to file:你的工程目录\catalina-base\logs
    这里写图片描述

可能会报错:
java.lang.ClassNotFoundException: websocket.drawboard.DrawboardContextListener 只需将/webapps/example 文件夹删除删除即可。


tomcat启动

tomcat的启动入口是org.apache.catalina.startup.Bootstrap.main()

Bootstrap.main()

//org.apache.catalina.startup.Bootstrap
public static void main(String args[]) {
         //创建一个Bootstrap,当bootstrap 初始化完成之后,再赋值给daemon
        if (daemon == null) {
            //try-catch 省略....
            Bootstrap bootstrap = new Bootstrap();
            //调用init()方法,初始化ClassLoader,并用ClassLoader穿件Catalina实例,赋给catalinaDaemon变量。
            bootstrap.init();
            daemon = bootstrap;
        } else {
            Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
        }

        try {
            //默认command为start
            String command = "start";
            if (args.length > 0) {
                //取args最后一个参数,为command
                command = args[args.length - 1];
            }

            if (command.equals("startd")) {
                args[args.length - 1] = "start";
                daemon.load(args);
                daemon.start();
            } else if (command.equals("start")) {
                //start比 startd多了一行代码: daemon.setAwait(true);
                daemon.setAwait(true);
                daemon.load(args);
                //调用start方法...
                daemon.start();
            }
            //省略,stopd,stop
            else if (command.equals("configtest")) {
                daemon.load(args);
                if (null==daemon.getServer()) {
                    System.exit(1);
                }
                System.exit(0);
            }
        } catch (Throwable t) {
            //省略log....
            System.exit(1);
        }

    }

main方法可传入的参数有
startd、stopd、start、stop,startd和start的区别是设置了一个await的boolean变量为true

main方法内容分为三部分:

  • 首先创建Bootstrap,并执行init()方法初始化,
  • 然后调用load()
  • 最后调用start()方法。

Bootstrap.init()
调用Boostrap的init方法主要完成:

  • 初始化路径:CATALINA_HOME,CATALINA_BASE
  • 初始化类加载器:commonLoader、catalinaLoader、sharedLoader.
  • 初始化Boostrap的Catalina对象:通过反射生成Catalina对象,并通过反射调用setParentClassLoader方法设置其父 ClassLoader为sharedLoader
//org.apache.catalina.startup.Bootstrap
public void init() throws Exception {
       //设置安装目录:CATALINA_HOME
       setCatalinaHome();

       //设置工作目录CATALINA_BASE
        setCatalinaBase();

       //初始化classLoaders
       initClassLoaders();

       //主线程的classLoader设置为catalinaLoader
       Thread.currentThread().setContextClassLoader(catalinaLoader);
      //安全管理的classLoad设置为catalineLoader
       SecurityClassLoad.securityClassLoad(catalinaLoader);

       //省略log....

      //load启动类Catalina
       Class<?> startupClass =catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
       Object startupInstance = startupClass.newInstance();

       /* 
        * 反射实现:catalina的ParentClassLoader设置为sharedLoader
        * catalina.setParentClassLoader(sharedLoader);
        */
       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;

   }

CATALINA_HOME和CATALINA_BASE区别?


CATALINA_HOME是Tomcat的安装目录,指向公用信息的位置,就是bin和lib的父目录。。
CATALINA_BASE是Tomcat的工作目录指向每个Tomcat目录私有信息的位置,就是conf、logs、temp、webapps和work的父目录。
这里写图片描述
如果我们想要运行Tomcat 的 多个实例,但是不想安装多个Tomcat软件副本。那么我们可以配置多个工作 目录,每个运行实例独占一个工作目录,但是共享同一个安装目录
tomcat多实例配置

Bootstrap.initClassLoaders()
tomcat的classloader通过conf/catalina.properties配置,配置内容为:

package.definition=sun.,java.,org.apache.catalina.,org.apache.coyote.,org.apache.tomcat.,org.apache.jasper.

##classloader 相关
common.loader=${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar
server.loader=
shared.loader=

//省略....

initClassLoaders()方法逻辑
初始化commonLoader ,catalinaLoader ,sharedLoader

//org.apache.catalina.startup.Bootstrap
private void initClassLoaders() {
      try {
          //commonLoader 设置父类加载器为null,则为jdk中的
           commonLoader = createClassLoader("common", null);
           if( commonLoader == null ) {
               //若没有配置文件配置,则取SystemClassLoader。
               commonLoader=this.getClass().getClassLoader();
           }

           //catalinaLoader , sharedLoader 设置父类加载器为 commonLoader
           catalinaLoader = createClassLoader("server", commonLoader);
           sharedLoader = createClassLoader("shared", commonLoader);
       } catch (Throwable t) {
           handleThrowable(t);
           log.error("Class loader creation threw exception", t);
           System.exit(1);
       }
   }

这里写图片描述

SecurityClassLoad.securityClassLoad()方法逻辑
使用catalinaLoader加载tomcat容器独享的一些calss,

//org.apache.catalina.security.SecurityClassLoad
public static void securityClassLoad(ClassLoader loader)
       throws Exception {

       if( System.getSecurityManager() == null ){
           return;
       }

       loadCorePackage(loader);//加载org.apache.catalina.core.下的class
       loadCoyotePackage(loader);//加载org.apache.coyote下的class
       loadLoaderPackage(loader);
       loadRealmPackage(loader);
       loadSessionPackage(loader);
       loadUtilPackage(loader);
       loadValvesPackage(loader);
       loadJavaxPackage(loader);
       loadConnectorPackage(loader);
       loadTomcatPackage(loader);
   }

Bootstrap.load()
Bootstrap.load最终调用的是Catalina的load方法.,主要完成:

  • 设置系统变量
  • 初始化命名系统
  • Digester类,默认将conf/server.xml解析成相应的对象,这个解析很重要,因为是他完成了Connector和Container的初始化工作。
  • Server调用init初始化声明周期,其父类LifecycleMBeanBase实现
//org.apache.catalina.startup.Catalina
private void load() {
    //当前面代码没有初始化CATALINA_BASE和CATALINA_HOME时,重新设置二者值
    initDirs();

    //初始化命名系统,即向System.Properties设置"java.naming.factory.initial"为"org.apache.naming.java.javaURLContextFactory"
    initNaming();

    //创建Digester,主要用于处理xml配置文件,创建Server,初始化Connector和Container
    Digester digester = createStartDigester();

    InputSource inputSource = null;
    InputStream inputStream = null;
    File file = null;
    try {
     //获取配置文件:默认值conf/server.xml,可由command参数-config指定
        file = configFile();
        inputStream = new FileInputStream(file);
        inputSource = new InputSource(file.toURI().toURL().toString());
    } //省略log,catch...

   //省略log,try-catch,部分代码.....

    try{
     inputSource.setByteStream(inputStream);
     digester.push(this);
    //解析xml
     digester.parse(inputSource);
    } //省略log,catch...

    getServer().setCatalina(this);

    //System.out , System.error
    initStreams();

    //调用LifecycleBase.init();最终调用自身的initInternal()方法,见后续`StandardServer`
    getServer().init();
}

Digester解析server.xml解析过程见: Tomcat—-Container源码分析

StandardServer.init()初始化流程见: Tomcat—-生命周期管理


Boostrap.start()
最终调用的是Catalina.start(),它主要完成:

  • Server加载:Server才是正真的tomcat服务执行者。调用load方法,初始化Connector和Container
  • 调用Server的start方法,最终调用的是StandardServer的startInternal方法,调用自己所有的Service的start方法,启动connector和container、excutor的start
  • 注册钩子
//org.apache.catalina.startup.Catalina
public void start() {
        if (getServer() == null) {
            //Server加载
            load();
        }

        try {
            //服务启动,//调用LifecycleBase.start();最终调用自身的startInternal()方法
            getServer().start();
        } //省略 catch,log.....

        // 注册shutdown钩子
        if (useShutdownHook) {
            if (shutdownHook == null) {
                shutdownHook = new CatalinaShutdownHook();
            }
            Runtime.getRuntime().addShutdownHook(shutdownHook);
            LogManager logManager = LogManager.getLogManager();
            if (logManager instanceof ClassLoaderLogManager) {
                ((ClassLoaderLogManager) logManager).setUseShutdownHook(
                        false);
            }
        }

        //进入等待状态
        if (await) {
            //调用Server.await()方法
            await(); 
            stop(); 
        }
    }

至此,tomat就启动完成了。
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值