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中还是要细致一些的。。。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值