Tomcat源码分析之ClassLoader部分的设计详细分析

读Tomcat的源码也算是有一段时间了吧,感觉读的也是断断续续的,这次写一篇比较综合性的吧,来看看Tomcat的整体ClassLoader体系的设计。。。。

在具体的涉及到源码之前,先来一张图来整体的描述一下整体的结构吧:



这张图在以前的文章应该也出现过。。。首先整个Tomcat的classLoader分为了两条线,左边的一条线为catalinaLoader,这个是Tomcat服务器专用的,用于加载Tomcat服务器本身的class,右边的一条线则为web应用程序用的,每一个web应用程序都有自己专用的WebappClassLoader,用于加载属于自己应用程序的资源,例如/web-inf/lib下面的jar包,classes里面的class文件。。。

然后上面也体现了整体的classLoader的双亲继承关系。。。。


好啦,接下来来开始进入代码部分吧。。在整个tomcat的启动入口部分bootstrap对象的main函数中,代码如下:

Bootstrap bootstrap = new Bootstrap();  //这里创建当前Bootstarp类型的对象
            try {
                bootstrap.init();  //初始化,这里有创建classLoader,其实这里主要是创建org.apache.catalina.startup.Catalina对象并调用setParentClassLoader设置classLoader,用的是shareLoader
            } catch (Throwable t) {
                handleThrowable(t);
                t.printStackTrace();
                return;
            }
            daemon = bootstrap;   //保存当前引用到静态变量

这里主要是对bootstrap的初始化,这里面就会涉及到commonLoader,catalinaLoader与sharedLoader的创建,来看代码吧:

 //初始化当前的tomcat后台,主要是创建org.apache.catalina.startup.Catalina对象,并且设置它的classLoader为catalinaLoader
    public void init() throws Exception {

        initClassLoaders();   //先初始化classLoader,包括common,catalina以及shared

        Thread.currentThread().setContextClassLoader(catalinaLoader);   //设置当前线程classLoader

        SecurityClassLoad.securityClassLoad(catalinaLoader);  //安全classLoader?

        // Load our startup class and call its process() method
        if (log.isDebugEnabled())
            log.debug("Loading startup class");
        Class<?> startupClass =
            catalinaLoader.loadClass  //这里加载catalina类型是用catalinaloader来加载的
            ("org.apache.catalina.startup.Catalina");  //获取org.apache.catalina.startup.Catalina类型
        Object startupInstance = startupClass.newInstance();  //创建org.apache.catalina.startup.Catalina对象

        // 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;  //传进这个classLoader
        Method method =
            startupInstance.getClass().getMethod(methodName, paramTypes);
        method.invoke(startupInstance, paramValues);  //调用刚刚创建的org.apache.catalina.startup.Catalina对象的setParentClassLoader设置classLoader,shareloader

        catalinaDaemon = startupInstance;  //将这个启动的实例保存起来,这里引用的是catalina的类型的对象

    }

这里有几个比较重要的部分吧,首先调用了initClassLoader方法来创建了上面提到的三个classLoader,然后这里还要注意,将当前的线程classLoader设置为了catalinaLoader,这个待会会看到具体干嘛用。。。接着就是调用创建的catalina对象的setParent方法,将sharedLoader传进去,这个也很重要。。一会就知道了。。。

这里还有一个比较重要的地方,catalina的class是用catalinaLoader加载的。。。

好了,先暂时搁置catalina部分的内容,来看看initClassLoader方法做了啥吧:

    //初始化classLoader,这里分别创建了3个classLoader,common,catalina和sharedLoader,其中common没有父亲,另外两个的父亲是common
    private void initClassLoaders() {
        try {
            commonLoader = createClassLoader("common", null);  //创建common的classloader  java.net.URLClassLoader
            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);    //java.net.URLClassLoader
            sharedLoader = createClassLoader("shared", commonLoader);    //java.net.URLClassLoader
        } catch (Throwable t) {
            handleThrowable(t);
            log.error("Class loader creation threw exception", t);
            System.exit(1);
        }
    }

这里就可以看到创建了commonLoader,catalinaLoader与sharedLoader,而且可以很清楚的看到他们之间的双亲结构。。。这里我们来看看createClassLoader方法是怎么搞的吧:

    //具体的创建classLoader的方法,第一个参数是当前要创建的loader的名字,第二个是这个loader的双亲loader的父亲
    private ClassLoader createClassLoader(String name, ClassLoader parent)
        throws Exception {
    	//首先获取要载入的资源路径
        String value = CatalinaProperties.getProperty(name + ".loader");    //"${catalina.base}/lib","${catalina.base}/lib/*.jar","${catalina.home}/lib","${catalina.home}/lib/*.jar"
        if ((value == null) || (value.equals("")))  //如果没有这个classLoader特定的资源,那么就用parent就好了
            return parent;

        value = replace(value);  //这里主要是获取当前classLoader加载资源的路径

        List<Repository> repositories = new ArrayList<>();

        String[] repositoryPaths = getPaths(value);  //将里面的路径区分开来

        for (String repository : repositoryPaths) {  //遍历所有的要载入的资源路径
            // Check for a JAR URL repository
        	/*
        	 * G:\work eclipse workspace\tomcat8/lib
				G:\work eclipse workspace\tomcat8/lib/*.jar
				G:\work eclipse workspace\tomcat8/lib
				G:\work eclipse workspace\tomcat8/lib/*.jar
        	 */
            try {
                @SuppressWarnings("unused")
                URL url = new URL(repository);  // 创建路径的url引用
                repositories.add(
                        new Repository(repository, RepositoryType.URL));  //将他们加入repositories
                continue;
            } catch (MalformedURLException e) {
                // Ignore
            }

            // Local repository
            if (repository.endsWith("*.jar")) {  //如果是*.jar结尾的,那么只取前面的文件夹路径就好了
                repository = repository.substring
                    (0, repository.length() - "*.jar".length());
                repositories.add(
                        new Repository(repository, RepositoryType.GLOB));
            } else if (repository.endsWith(".jar")) {  //如果是jar结尾的,那么表示是个jar包
                repositories.add(
                        new Repository(repository, RepositoryType.JAR));
            } else {
                repositories.add(
                        new Repository(repository, RepositoryType.DIR));  //其他的就是路径了
            }
        }

        return ClassLoaderFactory.createClassLoader(repositories, parent);   //创建classLoder
    }

第一个参数是要创建的classLoader的名字,第二个是该classLoader的父亲,对于commonLoader,它的父亲为null,这里首先做的是获取指定要创建的classLoader的配置信息,其实这里也就是该classLoder要载入的资源。。

这里也可以看到,如果classLoader没有自己要特定载入的资源的话,那么将不会创建,直接用parent就好了。。。

有的时候,对于sharedLoader与catalinaLoader就没有特定要创建的。。。在这种情况下就catalina,sharedLoader与commonLoader就为同一个对象。。。

当然对于最顶层的commonLoader,它是由自己要载入的资源的,路径如下:  //"${catalina.base}/lib","${catalina.base}/lib/*.jar","${catalina.home}/lib","${catalina.home}/lib/*.jar"


这也就是commonLoader要载入的主要的资源路径,因此也可以知道主要就是载入Tomcat服务器根路径下lib文件夹里面的资源。。。。最后调用ClassLoaderFactory的createClassLoader方法的时候将要载入的资源的路径引用穿件去就好了。。最终创建的classLoader其实就是类库的URLClassLoader,也就是在创建URLClassLoader对象的时候,将刚刚提到的资源的url引用传入就好了。。。。


那么到这里为止,commonLoader,catalinaLoader与sharedLoader的创建就算是比较清楚了吧。。。


接到开始的话题,在bootStrap对象的初始化方法中将当前的线程classLoader设置为了catalinaLoader,然后将创建的catalina对象的parentClassLoader设置为了sharedLoader。。。嗯。。记住。。。


好啦,接下来进入catalina对象部分,我们知道bootstrap对象的启动,实际上是调用catalina对象的start方法来具体工作的。。那么接下来进入catalina对象的start方法吧:

    //创建一个tomcatServer的实例,在bootstrap对象中的启动会调用catalina的start方法来启动tomcat
    public void start() {

        if (getServer() == null) {
            load();   //加载server,这里其实主要是解析xml文件,读取里面的元素定义,用于生成server对象,并且初始化server对象
        }

        if (getServer() == null) {
            log.fatal("Cannot start server. Server instance is not configured.");
            return;
        }

        long t1 = System.nanoTime();

        // Start the new server
        try {
            getServer().start();  //调用server的start,最终启动tomcat服务器,其实server要做的是启动里面的service
        } catch (LifecycleException e) {
            log.fatal(sm.getString("catalina.serverStartFail"), e);
            try {
                getServer().destroy();
            } catch (LifecycleException e1) {
                log.debug("destroy() failed for failed Server ", e1);
            }
            return;
        }

        long t2 = System.nanoTime();
        if(log.isInfoEnabled()) {
            log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");
        }

        // Register shutdown hook
        if (useShutdownHook) {
            if (shutdownHook == null) {
                shutdownHook = new CatalinaShutdownHook();
            }
            Runtime.getRuntime().addShutdownHook(shutdownHook);

            // If JULI is being used, disable JULI's shutdown hook since
            // shutdown hooks run in parallel and log messages may be lost
            // if JULI's hook completes before the CatalinaShutdownHook()
            LogManager logManager = LogManager.getLogManager();
            if (logManager instanceof ClassLoaderLogManager) {
                ((ClassLoaderLogManager) logManager).setUseShutdownHook(
                        false);
            }
        }

        if (await) {  
            await();  //阻塞当前线程
            stop();
        }
    }

这部分要做的事情,其实是创建server对象,然后启动就好了。。。然后catalina对象将会阻塞当前线程。。

那么来看看具体是怎么创建Server对象的吧:

    //用于创建server对象
    public void load() {

        long t1 = System.nanoTime();

        initDirs();  //初始化一些目录参数

        // Before digester - it may be needed

        initNaming();

        // Create and execute our Digester
        Digester digester = createStartDigester();  //创建处理xml文件的对象,并会生成相应的处理规则,例如如何创建server对象,主要是处理server.xml

        InputSource inputSource = null;
        InputStream inputStream = null;
        File file = null;
        try {
            file = configFile();   //获取server.xml
            inputStream = new FileInputStream(file);   //读取server.xml
            inputSource = new InputSource(file.toURI().toURL().toString());
        } catch (Exception e) {
            if (log.isDebugEnabled()) {
                log.debug(sm.getString("catalina.configFail", file), e);
            }
        }
        if (inputStream == null) {
            try {
                inputStream = getClass().getClassLoader()
                    .getResourceAsStream(getConfigFile());
                inputSource = new InputSource
                    (getClass().getClassLoader()
                     .getResource(getConfigFile()).toString());
            } catch (Exception e) {
                if (log.isDebugEnabled()) {
                    log.debug(sm.getString("catalina.configFail",
                            getConfigFile()), e);
                }
            }
        }

        // This should be included in catalina.jar
        // Alternative: don't bother with xml, just create it manually.
        if( inputStream==null ) {
            try {
                inputStream = getClass().getClassLoader()
                        .getResourceAsStream("server-embed.xml");
                inputSource = new InputSource
                (getClass().getClassLoader()
                        .getResource("server-embed.xml").toString());
            } catch (Exception e) {
                if (log.isDebugEnabled()) {
                    log.debug(sm.getString("catalina.configFail",
                            "server-embed.xml"), e);
                }
            }
        }


        if (inputStream == null || inputSource == null) {
            if  (file == null) {
                log.warn(sm.getString("catalina.configFail",
                        getConfigFile() + "] or [server-embed.xml]"));
            } else {
                log.warn(sm.getString("catalina.configFail",
                        file.getAbsolutePath()));
                if (file.exists() && !file.canRead()) {
                    log.warn("Permissions incorrect, read permission is not allowed on the file.");
                }
            }
            return;
        }

        try {
            inputSource.setByteStream(inputStream);
            digester.push(this);   //这里先将当前对象放到Digester的栈底,但会server生成之后会调用当前对象的setServer方法来保存起来
            digester.parse(inputSource);
        } catch (SAXParseException spe) {
            log.warn("Catalina.start using " + getConfigFile() + ": " +
                    spe.getMessage());
            return;
        } catch (Exception e) {
            log.warn("Catalina.start using " + getConfigFile() + ": " , e);
            return;
        } finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                // Ignore
            }
        }

        getServer().setCatalina(this);  //设置sever的catalina对象
        getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());  //设置server的目录
        getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());

        // Stream redirection
        initStreams();

        // Start the new server
        try {
            getServer().init();   //初始化创建的server对象,其实这里主要是初始化service
        } catch (LifecycleException e) {
            if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
                throw new java.lang.Error(e);
            } else {
                log.error("Catalina.start", e);
            }

        }

        long t2 = System.nanoTime();
        if(log.isInfoEnabled()) {
            log.info("Initialization processed in " + ((t2 - t1) / 1000000) + " ms");
        }

    }

这里其实主要就是创建Digester对象,然后用它来解析conf/server.xml文件,根据配置的信息来创建相应的对象,例如server对象,因此这个Digester就算是很重要的啦。。。来看看它的创建吧:

protected Digester createStartDigester() {
        long t1=System.currentTimeMillis();
        // Initialize the digester
        Digester digester = new Digester();   //这个对象用于解析处理xml文件
        digester.setValidating(false);
        digester.setRulesValidation(true);
        HashMap<Class<?>, List<String>> fakeAttributes = new HashMap<>();
        ArrayList<String> attrs = new ArrayList<>();
        attrs.add("className");
        fakeAttributes.put(Object.class, attrs);
        digester.setFakeAttributes(fakeAttributes);
        digester.setUseContextClassLoader(true);  //将useContextClassLoader参数设置为true,那么待会将会用预先保存的线程classLoader来载入class,这里其实就是catalinaloader

        // Configure the actions we will be using
        digester.addObjectCreate("Server",
                                 "org.apache.catalina.core.StandardServer",
                                 "className");   //创建一个创建server对象的规则
        digester.addSetProperties("Server");  //创建一个设置属性的规则,那么创建完了server对象之后就会设置配置文件后面的属性
        digester.addSetNext("Server",
                            "setServer",
                            "org.apache.catalina.Server");   //当server元素执行完了之后,执行的操作。。。这里其实是调用前面那个对象的方法,这里其实就是调用catalina对象的setServer方法

        digester.addObjectCreate("Server/GlobalNamingResources",
                                 "org.apache.catalina.deploy.NamingResourcesImpl");  //创建全局名字资源
        digester.addSetProperties("Server/GlobalNamingResources");
        digester.addSetNext("Server/GlobalNamingResources",
                            "setGlobalNamingResources",
                            "org.apache.catalina.deploy.NamingResourcesImpl");   //其实这里实在server对象上面设置resource

        digester.addObjectCreate("Server/Listener",
                                 null, // MUST be specified in the element
                                 "className");   //根据xml文件配置来创建listener
        digester.addSetProperties("Server/Listener");  //设置listener对象配置的属性
        digester.addSetNext("Server/Listener",
                            "addLifecycleListener",
                            "org.apache.catalina.LifecycleListener");  //这里其实是在server对象上面添加lifecyclelistener

        digester.addObjectCreate("Server/Service",
                                 "org.apache.catalina.core.StandardService",
                                 "className");    //创建service对象
        digester.addSetProperties("Server/Service");  //设置service的属性
        digester.addSetNext("Server/Service",
                            "addService",  //在server对象上面调用addService方法
                            "org.apache.catalina.Service");   //在server上面添加调用addService方法

        //为service创建listener,不一定有
        digester.addObjectCreate("Server/Service/Listener",
                                 null, // MUST be specified in the element
                                 "className");    //根据配置文件参数来创建service的listener
        digester.addSetProperties("Server/Service/Listener");
        digester.addSetNext("Server/Service/Listener",
                            "addLifecycleListener",
                            "org.apache.catalina.LifecycleListener");

        //Executor
        //创建service的executor,这个不一定有
        digester.addObjectCreate("Server/Service/Executor",
                         "org.apache.catalina.core.StandardThreadExecutor",
                         "className");
        digester.addSetProperties("Server/Service/Executor");

        digester.addSetNext("Server/Service/Executor",
                            "addExecutor",
                            "org.apache.catalina.Executor");

        //为servie添加connector规则
        digester.addRule("Server/Service/Connector",
                         new ConnectorCreateRule());    //这个规则会创建connector,而且如果有属性执行executor的话,还会设置connector的executor
        digester.addRule("Server/Service/Connector",  //<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443"/>
                         new SetAllPropertiesRule(new String[]{"executor"}));   //这里会将所有的属性都设置,除了executor
        digester.addSetNext("Server/Service/Connector",
                            "addConnector",
                            "org.apache.catalina.connector.Connector");  //在service上面添加这个connector


        //这里是设置connector的listener
        digester.addObjectCreate("Server/Service/Connector/Listener",
                                 null, // MUST be specified in the element
                                 "className");
        digester.addSetProperties("Server/Service/Connector/Listener");
        digester.addSetNext("Server/Service/Connector/Listener",
                            "addLifecycleListener",
                            "org.apache.catalina.LifecycleListener");

        // Add RuleSets for nested elements
        digester.addRuleSet(new NamingRuleSet("Server/GlobalNamingResources/"));
        digester.addRuleSet(new EngineRuleSet("Server/Service/"));   //engine元素的定义的处理,这里主要是创建eingie
        digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));  //engine里面host的定义
        digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));  //一些context的配置处理
        addClusterRuleSet(digester, "Server/Service/Engine/Host/Cluster/");
        digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/Context/"));

        // When the 'engine' is found, set the parentClassLoader.
        digester.addRule("Server/Service/Engine",
                         new SetParentClassLoaderRule(parentClassLoader));  //如果有发现engine,那么设置container(Engine对象)的parentClassloader,shareloader
        addClusterRuleSet(digester, "Server/Service/Engine/Cluster/");

        long t2=System.currentTimeMillis();
        if (log.isDebugEnabled()) {
            log.debug("Digester for server.xml created " + ( t2-t1 ));
        }        
        return (digester);

    }

这里可以其实主要就是对于server.xml文件的处理规则,有几点需要搞清楚:

(1) digester.setUseContextClassLoader(true);这句代码,将会对Digester进行设置,待会创建对象的时候加载class的时候将会用到当前线程的classLoader,这个在前面已经强调,当前线程classLoader被设置为了catalinaLoader

(2)来看看是如何创建对象的吧,这里对于对象的创建将会创建一个ObjectCreateRule规则,这里来看看这个规则是怎么处理的吧:

    //当遇到需要创建元素的element的时候要做的事情
    @Override
    public void begin(String namespace, String name, Attributes attributes)
            throws Exception {

        // Identify the name of the class to instantiate
        String realClassName = className;
        if (attributeName != null) {
            String value = attributes.getValue(attributeName);    //看是否有指定的属性
            if (value != null) {
                realClassName = value;
            }
        }
        if (digester.log.isDebugEnabled()) {
            digester.log.debug("[ObjectCreateRule]{" + digester.match +
                    "}New " + realClassName);
        }

        if (realClassName == null) {
            throw new NullPointerException("No class name specified for " +
                    namespace + " " + name);
        }

        // Instantiate the new object and push it on the context stack
        Class<?> clazz = digester.getClassLoader().loadClass(realClassName);  //用classloader来载入class
        Object instance = clazz.newInstance();  //创建这个对象
        digester.push(instance);  //放入digester
    }

这里其实主要就是调用classLoader的loadClass方法来加载class,然后创建对象。。。那么这里用的是什么classLoader呢。。?来看看:

   public ClassLoader getClassLoader() {

        if (this.classLoader != null) {
            return (this.classLoader);
        }
        if (this.useContextClassLoader) {  //这里一般都是用这里,线程classLoader,在bootstarp里面设置为catalinaLoader
            ClassLoader classLoader =
                    Thread.currentThread().getContextClassLoader();
            if (classLoader != null) {
                return (classLoader);
            }
        }
        return (this.getClass().getClassLoader());

    }

到这里就很清楚了吧,前面已经提到了,这里将会采用当前线程classLoader,也就是catalinaLoader,这里也就可以知道,对于Server对象所有东西的创建,其class的都是用的catalinaLoader。。

嗯。。。这也就是为啥说catalinaLoader是Tomcat服务器专用的了,整个Tomcat服务器的重要对象创建用到的loader都是它。。。

(3)注意一句代码: digester.addRule("Server/Service/Engine",
                         new SetParentClassLoaderRule(parentClassLoader));  //如果有发现engine,那么设置container(Engine对象)的parentClassloader,shareloader

它创建的规则将会将创建的engine对象的parentClassLoader设置为当前catalina对象的parentClassLoader,前面就已经说过了,当前catalina对象的parentClassLoader被设置为了sharedLoader,那么表示engine对象的parentClassLoader也会是sharedLoader。。。

(4)我们来看看创建host对象的用到的规则:

   	//创建host对象
        digester.addObjectCreate(prefix + "Host",
                                 "org.apache.catalina.core.StandardHost",  //创建host对象的配置
                                 "className");
        digester.addSetProperties(prefix + "Host");
        digester.addRule(prefix + "Host",
                         new CopyParentClassLoaderRule());  //会将host的parentClassloader设置为engine的,engine被设置为sharedloader

这里可以看到在host的创建中加了一个CopyParentClassLoaderRule规则,它的作用是将当前对象的parentClassLoader设置为上一层对象的parentClassLoader,host外面就是engine,那么可以知道将会将host的parentClassLoader也设置为sharedLoader。。。


好了,到这里catalina部分对classLoader的处理就差不多了。。。


到现在位置,我们知道在tomcat的启动的时候,载入tomcat系统相关的额class都是采用的catalinaLoader

然后还有在catalina对象中将parentClassLoader设置为了sharedLoader,然后engine的parentClassLoader也被设置为了sharedLoader,然后host对象的parentClassLoader也被设置为了sharedLoader,嗯,这个为以后context部分的内容埋下了伏笔。。。


好了,接下来就开始进入Context部分吧,我们知道一个Context基本上就代表了web应用程序,前面提到,每一个web应用程序都有自己专有的classLoader,那么接下来就来看看这个是如何创建的吧。。。在StandardContext的startInternal方法中,我们可以看到如下的代码:

        //loader部分
        if (getLoader() == null) { //这一步用于创建loader对象
            WebappLoader webappLoader = new WebappLoader(getParentClassLoader());    //这里用于创建当前context用的classLoader,这里会将parent设置为sharedclassLoader
            webappLoader.setDelegate(getDelegate());  
            setLoader(webappLoader);  //保存创建的loader
        }

这里是创建了WebappLoader对象,然后注意看这里传入了当前context对象的parentClassLoader,那么我们来看看这个方法:

    public ClassLoader getParentClassLoader() {
        if (parentClassLoader != null) {
            return (parentClassLoader);
        } if (getPrivileged()) {
            return this.getClass().getClassLoader();
        } else if (parent != null) {  //一般都是使用parent的parentClassLoader,也就是host的,host将会设置为与engine一样,也就是sharedLoader
            return (parent.getParentClassLoader());
        }
        return (ClassLoader.getSystemClassLoader());
    }

由于在创建context对象的时候并没有指定parentClassLoader,所以这里返回的将是context对象的parent的parentClassLoader,。我们知道context的parent是host对象,而host对象的parentclassLoader已经被设置为了sharedLoader,所以在这里创建webapploader的时候穿进去的构造参数其实也就是sharedLoader。。。

来看看WebappLoader的构造函数吧:

    public WebappLoader(ClassLoader parent) {
        super();
        this.parentClassLoader = parent;  //parentClassLoader,一般都是sharedLoader
    }

嗯,这里好像也就是主要将传进来的sharedLoader保存起来。。那么在来看看Webapploader对象是如何启动的吧:

    //启动webapploader
    protected void startInternal() throws LifecycleException {

        if (log.isDebugEnabled())
            log.debug(sm.getString("webappLoader.starting"));

        if (context.getResources() == null) {
            log.info("No resources for " + context);
            setState(LifecycleState.STARTING);
            return;
        }

        // Construct a class loader based on our current repositories list
        try {

            classLoader = createClassLoader();  //创建classLoader  org.apache.catalina.loader.WebappClassLoader
            classLoader.setResources(context.getResources());  //设置资源目录
            classLoader.setDelegate(this.delegate);

            // Configure our repositories
            setClassPath();  //设置classPath

            setPermissions();

            ((Lifecycle) classLoader).start();  //启动classLoader,里面主要是加载classes以及lib下的代码

            String contextName = context.getName();  //获取当前context的名字
            if (!contextName.startsWith("/")) {
                contextName = "/" + contextName;
            }
            ObjectName cloname = new ObjectName(context.getDomain() +
                    ":type=WebappClassLoader,host=" + context.getParent().getName() +
                    ",context=" + contextName);
            Registry.getRegistry(null, null)
                .registerComponent(classLoader, cloname, null);   //在jmx上面注册

        } catch (Throwable t) {
            t = ExceptionUtils.unwrapInvocationTargetException(t);
            ExceptionUtils.handleThrowable(t);
            log.error( "LifecycleException ", t );
            throw new LifecycleException("start: ", t);
        }

        setState(LifecycleState.STARTING);
    }

这里可以知道WebappLoader有一个classLoader的属性,将会在启动的时候创建,创建完了之后还会涉及到为这个classLoader设置资源,然后启动这个classLoader。。。

好啦,那就先来看看是如何创建的吧:

    //创建webappclassLoader,这里会将sharedLoader设置为parent
    private WebappClassLoader createClassLoader()
        throws Exception {

        Class<?> clazz = Class.forName(loaderClass);  //获取要创建的classLoader的class引用  org.apache.catalina.loader.WebappClassLoader
        WebappClassLoader classLoader = null;

        if (parentClassLoader == null) {
            parentClassLoader = context.getParentClassLoader();    //获取context的parentClassLoader,这里是sharedLoader
        }
        Class<?>[] argTypes = { ClassLoader.class };
        Object[] args = { parentClassLoader };
        Constructor<?> constr = clazz.getConstructor(argTypes);  //获取构造函数,这里需要传递一个parentClassLoader,其实这里的双亲loader就是sharedLoader
        classLoader = (WebappClassLoader) constr.newInstance(args);

        return classLoader;
    }

这里就很明白了,创建了一个WebappClassLoader对象,并且将其的双亲loader设置为了sharedLoader。。。。

其实到这里整个tomcat的整体的classLoader就算了解的差不多了。。

最后再来看看WebappClassLoader是怎么启动的吧:

    //其实主要是加载classes与lib下的代码,jar啥的
    public void start() throws LifecycleException {

        WebResource classes = resources.getResource("/WEB-INF/classes");  //获取/WEB-INF/classes目录的资源引用
        if (classes.isDirectory() && classes.canRead()) {
            addURL(classes.getURL());   //将该资源添加到当前classLoader的资源库
        }
        WebResource[] jars = resources.listResources("/WEB-INF/lib");  //这里是获取lib文件夹
        for (WebResource jar : jars) {  //遍历所有的资源 
            if (jar.getName().endsWith(".jar") && jar.isFile() && jar.canRead()) {
                addURL(jar.getURL());  // 将资源加入到classLoader的资源库
                jarModificationTimes.put(
                        jar.getName(), Long.valueOf(jar.getLastModified()));
            }
        }

        started = true;
        String encoding = null;
        try {
            encoding = System.getProperty("file.encoding");
        } catch (SecurityException e) {
            return;
        }
        if (encoding.indexOf("EBCDIC")!=-1) {
            needConvert = true;
        }

    }

这个代码看起来应该就很熟悉吧,加载/WEB-INF/classes以及/WEB-INF/lib下面的资源。。。


好啦。。classLoader部分就算完事了。。。可以看出tomcat8中对classLoader的处理比jetty6中还是要细致一些的。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值