Tomcat 源码分析-启动分析(2)

38 篇文章 0 订阅
27 篇文章 0 订阅

Tomcat 源码分析-启动分析(2)

Bootstrap 的main方法

启动脚本 startup.bat 开始到最后,所调用的就是Bootstrap.main()方法,接下来就来看一下这个方法。

源码以及简单说明

首先创建Bootstrap 类赋值到类本身的静态变量中,并初始化Bootstrap.init();

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 {//这里一堆的判断看一个就可以了,start的。
            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);  
                //load 方法中主要加载了server.xml。用Digester这个玩意来解析xml。
                daemon.start();
                //直接掉该接口的实现类WebappClassLoaderBase 的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);
        }
    }

main方法初始化Bootstrap之后,将参数加载到类中load(),并执行对应的.start()或者.stop()等方法;

接下来就,先重Bootstrap的初始化话方法,init()开始。

Bootstrap.init()初始化方法分析。

    public void init()
        throws Exception
    {
        // Set Catalina path 设置系统变量
        setCatalinaHome();
        setCatalinaBase();

        initClassLoaders();  // 初始化 类加载器  这个是重点 [接下来会分析一丢丢]
		//设置Tomcat的主线程类加载器为catalinaLoader
		//这里为了避免出现两个相同的类导致ClassNotFound异常,
		//使用上下文类加载器进行加载Tomcat自己的类
        Thread.currentThread().setContextClassLoader(catalinaLoader);
        SecurityClassLoad.securityClassLoad(catalinaLoader);
        // Load our startup class and call its process() method 
        // 负载启动类和调用过程()方法
        if (log.isDebugEnabled())
            log.debug("Loading startup class");
        Class<?> startupClass =
            catalinaLoader.loadClass
            ("org.apache.catalina.startup.Catalina");
        Object startupInstance = startupClass.newInstance();

        // Set the shared extensions class loader
        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);
		//调用 Catalina.setParentClassLoader()设置类加载器为shareLoader [同一个]
        catalinaDaemon = startupInstance;

    }

这里主要做的事情:

  • 设置 catalina.home、catalina.base 系统属性
  • initClassLoaders();:就是初始化commonLoader、catalinaLoader、sharedLoader类加载器
    • 这个初始化做的东西还是很多的,装载 catalina.properties 里配置的目录下的文件和jar包
    • 注册了 MBean,可以用于 JVM 监控该对象
  • 使用反射实例化 org.apache.catalina.startup.Catalina 对象,并赋值给静态成员 catalinaDaemon,以 sharedLoader 作为入参通过反射调用该对象的 setParentClassLoader 方法。

初始化类加载器:initClassLoaders()

先贴上部分源码:

 	ClassLoader commonLoader = null;
    ClassLoader catalinaLoader = null;
    ClassLoader sharedLoader = null;

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

这里初始化了三个类加载器, 而createClassLoader方法中则是根据传递进来的参数拼接.loader 读取conf目录下的catalina.properties文件中所对应的jar文件的目录而进行加载。

createClassLoader方法作为创建类加载的方法,给出部分源码,可以看看的】

createClassLoader部分代码:

 private ClassLoader createClassLoader(String name, ClassLoader parent)
        throws Exception {

        String value = CatalinaProperties.getProperty(name + ".loader");
     //vaule = ${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar
        if ((value == null) || (value.equals("")))
            return parent;
	// 如果读取server 和shared目录为空返回的还是传递进去的commonLoader
     。。。。
	return ClassLoaderFactory.createClassLoader(repositories, parent);
     //返回构建的对应目录的特权计算的类加载器[这个在里面的代码连类都不认识了o(╥﹏╥)o]
 }

加载完结果:

  common.loader="${catalina.base}/lib","${catalina.base}/lib/*.jar","${catalina.home}/lib","${catalina.home}/lib/*.jar"
    server.loader=common.loader
    shared.loader=common.loader
    //这里的结果3个加载器都是一样的

备注:server.loader 与 shared.loader 在旧版本里面是有对应目录的(tomcat5.5),新版本根目录就包含了所有的jar包了,所以另外两个就不用管了。

最后执行的是***load***方法和***start***方法了

daemon.load,daemon.start两个方法在最后被调用,这里daemon就是bootstrap,而他的load和start方法如下:

 	/**
     * Start the Catalina daemon.
     */
    public void start()
        throws Exception {
        if( catalinaDaemon==null ) init();
        Method method = catalinaDaemon.getClass().getMethod("start", (Class [] )null);
        method.invoke(catalinaDaemon, (Object [])null);
    }

catalinaDaemon 在初始化init()方法的时候已经创建了,这里的start 方法做做的内容就是了Catalina类的load和start 方法。

Bootstrap 的main方法,到这里就结束了,由于最后调用了Catalina的load和start方法,这就是接下来要分析的了。


参考资料

http://www.iocoder.cn/Tomcat/yuliu/Start-analysis-2-Bootstrap/

https://blog.csdn.net/dviewer/article/details/51814020


小杭
邮箱:xiao_hang0_0@163.com


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小_杭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值