tomcat7源码-Bootstrap的任务

tomcat的启动从bootstrap的main方法开始,在main方法中主要是做了三件事,调用init方法初始化自己,调用catalinaDaemon对象

的setAwait方法设置它的await属性为true,最后调用自己的start方法。

首先看看init方法:

  public void init()
        throws Exception
    {

        // Set Catalina path
        setCatalinaHome();
        setCatalinaBase();

        //初始化三个类加载器
        initClassLoaders();

        Thread.currentThread().setContextClassLoader(catalinaLoader);

        //加载conf下的catalina.properties配置文件下的common.loader和server.loader所指定的类。因为server.loader为空时
        //catalinaLoader和commonLoader是指向的同一对象,不为空时commonLoader是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);

        catalinaDaemon = startupInstance;

    }

这里的主要内容就是调用了initClassLoaders();初始化了commonLoader,catalinaLoader,sharedLoader三个类加载器,它们是bootstrap的

成员变量。并通过SecurityClassLoad.securityClassLoad(catalinaLoader)这步代码来加载conf/catalina.properties文件下common.loader,

server.loader,shared.loade三个属性指定的jar包和class。securityClassLoad会通过一系列环境变量替换等处理来找到相关的类并加载它们。

最后一段代码初始化Catalina对象,再设置它的父类加载器为sharedLoader。这里的一些疑问稍后再说。

先看看initClassLoaders()方法:

initClassLoaders源码:
   private void initClassLoaders() {
        try {
            commonLoader = createClassLoader("common", null);//1,创建commonLoader类加载器
            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);//2,
            sharedLoader = createClassLoader("shared", commonLoader);//3,
        } catch (Throwable t) {
            handleThrowable(t);
            log.error("Class loader creation threw exception", t);
            System.exit(1);
        }
    }

第一步是通过common关键字初始化了一个commonLoader对象。

进到createClassLoader方法中:

 private ClassLoader createClassLoader(String name, ClassLoader parent)
        throws Exception {
        String value = CatalinaProperties.getProperty(name + ".loader");
        if ((value == null) || (value.equals("")))
            return parent;
        value = replace(value);
        ........
        ClassLoader classLoader = ClassLoaderFactory.createClassLoader
            (repositories, parent);
        ........
        return classLoader;
    }

这里应该很清楚,CatalinaProperties.getProperty(name + ".loader");就是获取配置文件里的属性的方法。value = replace(value)替换环境变量

等一系列的转换获取了一些类路径的url最后将这些url传给ClassLoaderFactory.createClassLoader方法。CommonclassLoader和其他两个类加载器都

是通过该方法获取的,它们是一个StandardClassLoader实例。StandardClassLoader只是简单的从URLClassLoader继承,并实现了

StandardClassLoaderMBean接口来实现JMX监控。所以可以认为这三个类加载器就是URLClassLoader对象,

createClassLoader(repositories, parent),其实就是调用了URLClassLoader的构造方法,传入类路径的URL集合和父加载器。如果打开

conf/catalina.properties配置文件,server.loader,shared.loade默认为空。从createClassLoader方法中if判断就可以知道

commonLoader == catalinaLoader == sharedLoader。

这里先说一点设置这三个类加载器的用意,以后再用源码证明。commonLoader被用来设置为catalinaLoader和sharedLoader的父加载器。

catalinaLoader用来加载tomcat自身程序所需要的类。sharedLoader用来加载一些webapp目录下的程序共享的class,并被WebappClassLoader

(用户web程序的类加载器)设置为父类(从这里就可以知道web程序的类是怎么实现相互隔离,但又可以共享某些jar包的。因为类加载器在加载

类时会先让父加载器去加载,父加载器找不到的话就自己来加载,在这里sharedLoader和WebappClassLoader都有不同的加载路径,所有的

WebappClassLoader都继承自sharedLoader,所以他们共享了sharedLoader的加载路径)。

再来看看SecurityClassLoad.securityClassLoad(catalinaLoader):

public static void securityClassLoad(ClassLoader loader)
        throws Exception {

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

        loadCorePackage(loader);
        loadCoyotePackage(loader);
        loadLoaderPackage(loader);
        loadRealmPackage(loader);
        loadServletsPackage(loader);
        loadSessionPackage(loader);
        loadUtilPackage(loader);
        loadValvesPackage(loader);
        loadJavaxPackage(loader);
        loadConnectorPackage(loader);
        loadTomcatPackage(loader);
    }

可以进每个方法看下,它是加载了指定的一些类,注意这些类名是指定的,如果你放了自己的jar包在里面,那么它的加载就不是catalinaLoader完成的。

最后就是初始化catalinaDaemon对象了,从最后一段代码可以看出catalinaDaemon被赋值为org.apache.catalina.startup.Catalina的实例,

并且通过反射调用调用了setParentClassLoader方法,参数是catalinaLoader。

这里设置父加载器的用意是什么?下面是这个方法的源码

 public void setParentClassLoader(ClassLoader parentClassLoader) {
        this.parentClassLoader = parentClassLoader;
    }

方法很简单,看来问题不在这。在来看看Catalina的另一个方法:

  public ClassLoader getParentClassLoader() {
        if (parentClassLoader != null) {
            return (parentClassLoader);
        }
        return ClassLoader.getSystemClassLoader();
    }

其实这里parentClassLoader属性不是说加载Catalina的类加载器的父类加载器,它只是一个普通的属性。

在来看看哪里调用了getParentClassLoader,在StandardServer中有这样一个方法:

  @Override
    public ClassLoader getParentClassLoader() {
        if (parentClassLoader != null)
            return (parentClassLoader);
        if (catalina != null) {
            return (catalina.getParentClassLoader());
        }
        return (ClassLoader.getSystemClassLoader());
    }

然后他的下级组件StandardService:

  @Override
    public ClassLoader getParentClassLoader() {
        if (parentClassLoader != null)
            return (parentClassLoader);
        if (server != null) {
            return (server.getParentClassLoader());
        }
        return (ClassLoader.getSystemClassLoader());
    }

与之对应的都有一个setParentClassLoader方法。

engin容器也有这个方法。最后在WebappLoader中有个如下的方法:

 private WebappClassLoader createClassLoader()
        throws Exception {

        Class<?> clazz = Class.forName(loaderClass);
        WebappClassLoader classLoader = null;

        if (parentClassLoader == null) {
            parentClassLoader = container.getParentClassLoader();
        }
        Class<?>[] argTypes = { ClassLoader.class };
        Object[] args = { parentClassLoader };
        Constructor<?> constr = clazz.getConstructor(argTypes);
        classLoader = (WebappClassLoader) constr.newInstance(args);

        return classLoader;

    }

在这个方法中调用了org.apache.catalina.loader.WebappClassLoader的构造方法将容器container的parentClassLoader的属性最为classLoader的

父类加载器。这里可以得出结论从 Server 》 service 》 engin 》container没一个级别都在尝试设置parentClassLoader的值,而parentClassLoader

将作为WebappClassLoader的父类加载器。也就是说parentClassLoader是针对WebappClassLoader而言的,而每一级别都在尝试修改它。·

现在已经介绍完了bootstrap的init方法,下一步是来介绍setAwait(true)方法的作用。通过反射设置了Catalina的await属性为true。Catalina成功启动后

会通过检查该值来调用它的成员变量server的await方法:

public void start() {
    ........
        if (await) {
            await();
            stop();
        }
    }
 public void await() {

        getServer().await();

    }

Catalina的成员变量server是一个StandardServer对象,通过调用该对象的await方法,会让他一直监控server.xml配置文件中的这段配置:

<Server port="8005" shutdown="SHUTDOWN">,所定义的端口发送过来的命令。

最后看看start方法:

public void start()
    throws Exception {
    if( catalinaDaemon==null ) init();

    Method method = catalinaDaemon.getClass().getMethod("start", (Class [] )null);
    method.invoke(catalinaDaemon, (Object [])null);

}

start最终通过反射调用了catalinaDaemon(也就是Catalina)的start方法。(这里看到的多处位置是通过反射调用catalinaDaemon的方法,

因为catalinaDaemon是一个object的引用指向的Catalina对象所有它只能调用object对象的方法,想要调用Catalina的方法只能通过反射。)

总结:bootstrap的主要任务就是初始化commonLoader,catalinaLoader,sharedLoader,通过catalinaLoader加载所需要的类,

初始化catalinaDaemon对象,调用它的start方法。

疑问:bin/bootstrap.jar 和lib/catalina.jar中都有bootstrap,脚本调用的是bootstrap.jar中的main方法,bootstrap在初始三个类加载器后,

加载类时并没有加载org.apache.catalina.startup包下的类,也就是说虽然两个jar包都含有bootstrap类,但只是系统的类加载器加载了

bin/bootstrap.jar里的bootstrap。而其他lib 目录下的所有的类都是有bootstrap里面的三个类加载器加载的。不清楚为什么这样做。

时间: 2024-10-06 16:48:20

tomcat7源码-Bootstrap的任务的相关文章

[tomcat7源码学习]初始化之catalina.home和catalina.base(转)

我们在代码中为了获取某个配置文件路径下的文件经常会这么写 String tomcatPath = System.getProperty("catalina.home") + "/webapps/axis2/WEB-INF/conf/"; tomcatPath = tomcatPath.replace("/", File.separator); //使用此方法是为了区分unix系统与windows, //File.separator UNIX中为/

死磕Tomcat7源码之二:web组件初始化

经过死磕Tomcat7源码之一:解析web.xml,已经知道webapp的配置信息是如何解析到内存中.接下来,就是如何将对应的组件对象初始化化.分析所有的组件初始化过程,根本不可能.本文重点针对阐明3个主要组件的初始化过程,分别是:servlet,listener,filter.通过本文,你可以掌握以下知识点 了解组件初始化调用序列 组件servlet,listener,filter组件的初始化顺序 listener的初始化过程 servlet的初始化过程 filter的初始化过程 1.组件初始

转:tomcat7源码导入Eclipse

本文转自:http://blog.csdn.net/nacey5201/article/details/9793097 1.下载tomcat源码.建议下载最新版本tomcat7. svn地址:http://svn.apache.org/repos/asf/tomcat/tc7.0.x/trunk 2.由于tomcat采用ant编译.故需下载ant.如果已经下载此工具,则直接跳过. ant下载地址:http://ant.apache.org/bindownload.cgi 3.环境变量设置.把an

springmvc整合mybatis框架源码 bootstrap html5 mysql oracle

获取[下载地址]   QQ: 313596790   [免费支持更新]A 代码生成器(开发利器);全部是源码     增删改查的处理类,service层,mybatis的xml,SQL( mysql   和oracle)脚本,   jsp页面 都生成   就不用写搬砖的代码了,生成的放到项目里,可以直接运行B 阿里巴巴数据库连接池druid;  数据库连接池  阿里巴巴的 druid.Druid在监控.可扩展性.稳定性和性能方面都有明显的优势C 安全权限框架shiro ;  Shiro 是一个用

springmvc整合mybatis框架源码 bootstrap html5 mysql oracle maven SSM SSH

获取[下载地址]   QQ: 313596790   [免费支持更新]A 代码生成器(开发利器);全部是源码     增删改查的处理类,service层,mybatis的xml,SQL( mysql   和oracle)脚本,   jsp页面 都生成   就不用写搬砖的代码了,生成的放到项目里,可以直接运行B 阿里巴巴数据库连接池druid;  数据库连接池  阿里巴巴的 druid.Druid在监控.可扩展性.稳定性和性能方面都有明显的优势C 安全权限框架shiro ;  Shiro 是一个用

springmvc mybatis 整合 框架源码 bootstrap html5 mysql oracle spring

获取[下载地址]   QQ: 313596790   [免费支持更新]A 代码生成器(开发利器);全部是源码     增删改查的处理类,service层,mybatis的xml,SQL( mysql   和oracle)脚本,   jsp页面 都生成   就不用写搬砖的代码了,生成的放到项目里,可以直接运行B 阿里巴巴数据库连接池druid;  数据库连接池  阿里巴巴的 druid.Druid在监控.可扩展性.稳定性和性能方面都有明显的优势C 安全权限框架shiro ;  Shiro 是一个用

java后台框架 springmvc整合mybatis框架源码 bootstrap html5 mysql oracle

获取[下载地址]   QQ: 313596790   [免费支持更新]A 代码生成器(开发利器);全部是源码     增删改查的处理类,service层,mybatis的xml,SQL( mysql   和oracle)脚本,   jsp页面 都生成   就不用写搬砖的代码了,生成的放到项目里,可以直接运行B 阿里巴巴数据库连接池druid;  数据库连接池  阿里巴巴的 druid.Druid在监控.可扩展性.稳定性和性能方面都有明显的优势C 安全权限框架shiro ;  Shiro 是一个用

源码|Bootstrap、HTML5、Spring MVC、Mybatis、Hibernate、Java

A.代码生成器(开发利器) 生成Java各层次的类和JSP等文件,提高开发效率 B.阿里巴巴数据库连接池Druid 性能最好的数据库连接池,稳定.可扩展.高性能.高并发 C.安全权限框架Shiro 实现认证.授权.加密.缓存.并发.会话管理.单点登录等功能 D.Ehcache二级缓存和Spring MVC静态加载缓存 E.微信接口开发 详尽的单元测试代码,详尽的开发文档,每个模块都有详尽说明和代码示例 F.提供基于JBPM工作流的OA办公系统(后续加入Activiti 5.18工作流) ----

Ace 1.3.1 网站管理后台源码 Bootstrap响应式模板主题

Ace 响应式管理后台模板最新版本v1.3.1同步升级,欢迎下载使用,注意:本站提供为官网购买压缩源码版,在官网上用18$买来,现只售20元,可以帮亲省3/4的额外开销哦,需要的亲可以到本博的淘宝小店购买,包升级(免费!). Ace简介: Ace (v1.3.1)是一个轻量.功能丰富.HTML5.响应式.支持手机及平板电脑上浏览的管理后台模板,基于CSS框架Bootstrap制作,Bootstrap版本更新至 3.0,Ace – Responsive Admin Template当前最新版! 淘