Tomcat源码分析 (五)

  • 时间:
  • 浏览:1
  • 来源:极速快3_快3在线稳定计划_极速快3在线稳定计划

在研究tomcat 类加载前一天,亲戚亲戚让我门让我门让我门复习一下之后 说巩固一下java 默认的类加载器。楼主前一天对类加载也是懵懵懂懂,借此之后 ,也好好复习一下。

楼主翻开了神书《深入理解Java虚拟机》第二版,p227, 关于类加载器的偏离 。请看:

哪些是类加载机制?

Java虚拟机把描述类的数据从Class文件加载进内存,并对数据进行校验,转换解析和初始化,最终形成还还要呗虚拟机直接使用的Java类型,这全都虚拟机的类加载机制。

虚拟机设计团队把类加载阶段中的“通过另一四个 类的全限定名来获取描述此类的二进制字节流”这人 动作倒进Java虚拟机内部去实现,以便让应用应用进程这人 人决定怎么能否去获取所还要的类。实现这动作的代码模块成为“类加载器”。

类与类加载器的关系

类加载器不言而喻只用于实现类的加载动作,但它在Java应用进程中起到的作用却远远不限于类加载阶段。对于任意另一四个 类,都还要由加载他的类加载器和这人 类四种 一同确立其在Java虚拟机中的唯一性,每另一四个 类加载器,都拥另一四个 独立的类命名空间。这句话还还要表达的更通俗这人 :比较另一四个 类是有无“相等”,只有在这另一四个 类是由同另一四个 类加载器加载的前提下才有意义,之后 ,即使这另一四个 类来自同另一四个 Class文件,被同另一四个 虚拟机加载,之后 加载亲戚亲戚让我门让我门让我门的类加载器不同,那这人 另一四个 类就必定不相等。

哪些是双亲委任模型?

  1. 从Java虚拟机的深度来说,只居于四种 不类事加载器:四种 是启动类加载器(Bootstrap ClassLoader),这人 类加载器使用C++语言实现(只限HotSpot),是虚拟机自身的一偏离 ;另四种 全都所有这人 的类加载器,哪些类加载器都由Java语言实现,独立于虚拟机内部,之后 全都继承自抽象类java.lang.ClassLoader.

  2. 从Java开发人员的深度来看,类加载还还还要划分的更细致这人 ,绝大偏离 Java应用进程员都会使用以下3种系统提供的类加载器:

    • 启动类加载器(Bootstrap ClassLoader):这人 类加载器复杂性将存倒进 JAVA_HOME/lib 目录中的,之后 被-Xbootclasspath 参数所指定的路径种的,之后 是虚拟机识别的(仅按照文件名识别,如rt.jar,名字不符合的类库即使倒进lib目录下全都会重载)。
    • 扩展类加载器(Extension ClassLoader):这人 类加载器由sun.misc.Launcher$ExtClassLoader实现,它负责夹杂JAVA_HOME/lib/ext 目录下的,之后 被java.ext.dirs 系统变量所指定的路径种的所有类库。开发者还还要直接使用扩展类加载器。
    • 应用应用进程类加载器(Application ClassLoader):这人 类加载器由sun.misc.Launcher$AppClassLoader 实现。之后 这人 类加载器是ClassLoader 种的getSystemClassLoader依据的返回值,全都也成为系统类加载器。它负责加载用户类路径(ClassPath)上所指定的类库。开发者还还要直接使用这人 类加载器,之后 应用中这么定义过这人 人的类加载器,一般情况表下这人 全都应用进程中默认的类加载器。

哪些类加载器之间的关系一般如下图所示:

图中各个类加载器之间的关系成为 类加载器的双亲委派模型(Parents Dlegation Mode)。双亲委派模型要求除了顶层的启动类加载器之外,其余的类加载器都应当由这人 人的父类加载器加载,这里类加载器之间的父子关系一般不需要以继承的关系来实现,全都都使用组合关系来复用父加载器的代码。

类加载器的双亲委派模型在JDK1.2 期间被引入并被广泛应用于前一天的所有Java应用进程中,但他并都会 个强制性的约束模型,全都Java设计者推荐给开发者的四种 类加载器实现依据。

双亲委派模型的工作过程是:之后 另一四个 类加载器收到了类加载的请求,他首先不需要这人 人去尝试加载这人 类,全都把这人 请求委派父类加载器去完成。每另一四个 层次的类加载器都会 这么,之后 所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈这人 人无法完成这人 请求(他的搜索范围中这么找到所需的类)时,子加载器才会尝试这人 人去加载。

 为哪些要这么做呢?

 之后 这么使用双亲委派模型,由各个类加载器自行加载说说,之后 用户这人 人编写了另一四个 称为java.lang.Object的类,并倒应用应用进程序的ClassPath中,那系统之后 跳出多个不同的Object类, Java类型体系中最基础的行为就无法保证。应用应用进程也之后 变得一片混乱。

双亲委任模型时怎么能否实现的?

非常简单:所有的代码都会 java.lang.ClassLoader中的loadClass依据之中,代码如下:

先检查是有无之后 被加载过,若这么加载则调用父加载器的loadClass依据, 如父加载器为空则默认使用启动类加载器作为父加载器。之后 父类加载失败,抛出ClassNotFoundException 异常后,再调用这人 人的findClass依据进行加载。

怎么能否破坏双亲委任模型?

双亲委任模型都会 另一四个 强制性的约束模型,全都另一四个 建议型的类加载器实现依据。在Java的世界中大偏离 的类加载器都遵循者模型,但都会 例外,到目前为止,双亲委派模型有过3次大规模的“被破坏”的情况表。

第一次:在双亲委派模型跳出前一天-----即JDK1.2发布前一天。

第二次:是这人 模型自身的缺陷导致 分析的。亲戚亲戚让我门让我门让我门说,双亲委派模型很好的处里了各个类加载器的基础类的统一问提(越基础的类由越上层的加载器进行加载),基础类不言而喻称为“基础”,是之后 它们时不时 作为被用户代码调用的API, 但这么绝对,之后 基础类调用会用户的代码怎么能办呢?

这都会 这么之后 的。另一四个 典型的例子全都JNDI服务,JNDI现在之后 是Java的标准服务,它的代码由启动类加载器去加载(在JDK1.3时就倒进去的rt.jar),但它还要调用由独立厂商实现并部署在应用应用进程的ClassPath下的JNDI接口提供者(SPI, Service Provider Interface)的代码,但启动类加载器太少是“认识“哪些代码啊。之后 哪些类这么rt.jar中,之后 启动类加载器又还要加载。怎么能办呢?

为了处里这人 问提,Java设计团队只好引入了另一四个 不太优雅的设计:应用进程上下文类加载器(Thread Context ClassLoader)。这人 类加载器还还要通过java.lang.Thread类的setContextClassLoader依据进行设置。之后 创建应用进程时还未设置,它之后 从父应用进程中继承另一四个 ,之后 在应用应用进程的全局范围内都这么设置太少说说,那这人 类加载器默认即使应用应用进程类加载器。

嘿嘿,有了应用进程上下文加载器,JNDI服务使用这人 应用进程上下文加载器去加载所还要的SPI代码,也全都父类加载器请求子类加载器去完成类加载的动作,这人 行为实际上全都打通了双亲委派模型的层次型态来逆向使用类加载器,实际上之后 违背了双亲委派模型的一般性原则。但这无可奈何,Java中所有涉及SPI的加载动作基本胜都采用这人 依据。类事JNDI,JDBC,JCE,JAXB,JBI等。

第三次:为了实现热插拔,热部署,模块化,意思是再加另一四个 功能或减去另一四个 功能不需要重启,只还要把这模块连类事加载器一同换掉就实现了代码的热替换。

Tomcat 的类加载器是怎么能设计的?

首先,亲戚亲戚让我门让我门让我门来问个问提:

Tomcat 之后 使用默认的类加载机制行不行?

亲戚亲戚让我门让我门让我门思考一下:Tomcat是个web容器, 这么它要处里哪些问提:

  1. 另一四个 web容器之后 还要部署另一四个 应用应用进程,不同的应用应用进程之后 会依赖同另一四个 第三方类库的不同版本,只有要求同另一四个 类库在同另一四个 服务器只有一份,之后 要保证每个应用应用进程的类库都会 独立的,保证相互隔离。
  2. 部署在同另一四个 web容器中相同的类库相同的版本还还要共享。之后 ,之后 服务器有10个应用应用进程,这么要有10份相同的类库加载进虚拟机,这是扯淡的。
  3. web容器都会 这人 人依赖的类库,只有于应用应用进程的类库混淆。基于安全考虑,应该让容器的类库和应用进程的类库隔抛弃来。
  4. web容器要支持jsp的修改,亲戚亲戚让我门让我门让我门知道,jsp 文件最终也是要编译成class文件能否 在虚拟机中运行,但应用应用进程后修改jsp之后 是司空见惯的事情,之后 要你何用? 全都,web容器还要支持 jsp 修改后不需要重启。

再看看亲戚亲戚让我门让我门让我门的问提:Tomcat 之后 使用默认的类加载机制行不行?

答案是不行的。为哪些?亲戚亲戚让我门让我门让我门看,第另一四个 问提,之后 使用默认的类加载器机制,这么是无法加载另一四个 相类事库的不同版本的,默认的类加器是不管你是哪些版本的,只在乎你的全限定类名,之后 只有一份。第四个问提,默认的类加载器是能否 实现的,之后 他的职责全都保证唯一性。第另一四个 问提和第另一四个 问提一样。亲戚亲戚让我门让我门让我门再看第四个问提,亲戚亲戚让我门让我门让我门想亲戚亲戚让我门让我门让我门要怎么能实现jsp文件的热修改(楼主起的名字),jsp 文件不言而喻也全都class文件,这么之后 修改了,但类名还是一样,类加载器会直接取依据区中之后 居于的,修改后的jsp是不需要重新加载的。这么怎么能办呢?亲戚亲戚让我门让我门让我门还还要直接卸载掉这jsp文件的类加载器,全都你应该想到了,每个jsp文件对应另一四个 唯一的类加载器,当另一四个 jsp文件修改了,就直接卸载这人 jsp类加载器。重新创建类加载器,重新加载jsp文件。

Tomcat 怎么能否实现这人 人独特的类加载机制?

亲戚亲戚让我门让我门让我门看看亲戚亲戚让我门让我门让我门的设计图:

亲戚亲戚让我门让我门让我门在这张图中看得人全都类加载器,除了Jdk自带的类加载器,亲戚亲戚让我门让我门让我门尤其关心Tomcat自身持有的类加载器。仔细这人 亲戚亲戚让我门让我门让我门很容易发现:Catalina类加载器和Shared类加载器,亲戚亲戚让我门让我门让我门并都会 父子关系,全都兄弟关系。怎么能原先设计,亲戚亲戚让我门让我门让我门得分析一下每个类加载器的用途,能否 知晓。

  1. Common类加载器,负责加载Tomcat和Web应用都复用的类
  2. Catalina类加载器,负责加载Tomcat专用的类,而哪些被加载的类在Web应用中将不可见
  3. Shared类加载器,负责加载Tomcat下所有的Web应用应用进程都复用的类,而哪些被加载的类在Tomcat中将不可见
  4. WebApp类加载器,负责加载具体的某个Web应用应用进程所使用到的类,而哪些被加载的类在Tomcat和这人 的Web应用应用进程都将不可见
  5. Jsp类加载器,每个jsp页面另一四个 类加载器,不同的jsp页面有不同的类加载器,方便实现jsp页面的热插拔

源码阅读

Tomcat启动的入口在Bootstrap的main()依据main()依据执行前,必然先执行其static{}块。全都亲戚亲戚让我门让我门让我门首先分析static{}块,之后 分析main()依据

Bootstrap.static{}

static {
    // 获取用户目录
    // Will always be non-null
    String userDir = System.getProperty("user.dir");

    // 第一步,从环境变量中获取catalina.home,在这么获取到的前一天将执行顶端的获取操作
    // Home first
    String home = System.getProperty(Globals.CATALINA_HOME_PROP);
    File homeFile = null;

    if (home != null) {
        File f = new File(home);
        try {
            homeFile = f.getCanonicalFile();
        } catch (IOException ioe) {
            homeFile = f.getAbsoluteFile();
        }
    }

    // 第二步,在第一步没获取的前一天,从bootstrap.jar所在目录的上一级目录获取
    if (homeFile == null) {
        // First fall-back. See if current directory is a bin directory
        // in a normal Tomcat install
        File bootstrapJar = new File(userDir, "bootstrap.jar");

        if (bootstrapJar.exists()) {
            File f = new File(userDir, "..");
            try {
                homeFile = f.getCanonicalFile();
            } catch (IOException ioe) {
                homeFile = f.getAbsoluteFile();
            }
        }
    }

    // 第三步,第二步中的bootstrap.jar之后

不居于,这时亲戚亲戚让我门让我门让我门直接把user.dir作为亲戚亲戚让我门让我门让我门的home目录
    if (homeFile == null) {
        // Second fall-back. Use current directory
        File f = new File(userDir);
        try {
            homeFile = f.getCanonicalFile();
        } catch (IOException ioe) {
            homeFile = f.getAbsoluteFile();
        }
    }

    // 重新设置catalinaHome属性
    catalinaHomeFile = homeFile;
    System.setProperty(
            Globals.CATALINA_HOME_PROP, catalinaHomeFile.getPath());

    // 接下来获取CATALINA_BASE(从系统变量中获取),若不居于,则将CATALINA_BASE保持和CATALINA_HOME相同
    // Then base
    String base = System.getProperty(Globals.CATALINA_BASE_PROP);
    if (base == null) {
        catalinaBaseFile = catalinaHomeFile;
    } else {
        File baseFile = new File(base);
        try {
            baseFile = baseFile.getCanonicalFile();
        } catch (IOException ioe) {
            baseFile = baseFile.getAbsoluteFile();
        }
        catalinaBaseFile = baseFile;
    }
   // 重新设置catalinaBase属性
    System.setProperty(
            Globals.CATALINA_BASE_PROP, catalinaBaseFile.getPath());
}

亲戚亲戚让我门让我门让我门把代码中的注释搬下来总结一下:

  1. 获取用户目录
  2. 第一步,从环境变量中获取catalina.home,在这么获取到的前一天将执行顶端的获取操作
  3. 第二步,在第一步没获取的前一天,从bootstrap.jar所在目录的上一级目录获取
  4. 第三步,第二步中的bootstrap.jar之后 不居于,这时亲戚亲戚让我门让我门让我门直接把user.dir作为亲戚亲戚让我门让我门让我门的home目录
  5. 重新设置catalinaHome属性
  6. 接下来获取CATALINA_BASE(从系统变量中获取),若不居于,则将CATALINA_BASE保持和CATALINA_HOME相同
  7. 重新设置catalinaBase属性

简单总结一下,全都加载并设置catalinaHome和catalinaBase相关的信息,以备后续使用。

main()

main依据大体分成两块,一块为init,另一块为load+start

public static void main(String args[]) {
    // 第一块,main依据第一次执行的前一天,daemon肯定为null,全都直接new了另一四个

Bootstrap对象,之后

执行其init()依据
    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
        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);
    }

    // 第二块,执行守护对象的load依据和start依据
    try {
        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);
            daemon.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);
    }
}

亲戚亲戚让我门让我门让我门点到init()顶端去看看~

public void init() throws Exception {
    // 非常关键的地方,初始化类加载器s,顶端亲戚亲戚让我门让我门让我门会完正具体地分析这人

依据
    initClassLoaders();

    // 设置上下文类加载器为catalinaLoader,这人

类加载器负责加载Tomcat专用的类
    Thread.currentThread().setContextClassLoader(catalinaLoader);
    // 暂时略过,顶端会讲
    SecurityClassLoad.securityClassLoad(catalinaLoader);

    // 使用catalinaLoader加载亲戚亲戚让我门让我门让我门的Catalina类
    // 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.getConstructor().newInstance();

    // 设置Catalina类的parentClassLoader属性为sharedLoader
    // 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守护对象为刚才使用catalinaLoader加载类、并初始化出来的Catalina对象
    catalinaDaemon = startupInstance;
}

关键的依据initClassLoaders,这人 依据负责初始化Tomcat的类加载器。通过这人 依据,亲戚亲戚让我门让我门让我门很容易验证亲戚亲戚让我门让我门让我门上一小节提到的Tomcat类加载图。

private void initClassLoaders() {
    try {
        // 创建commonLoader,之后

未创建成果说说,则使用应用应用进程类加载器作为commonLoader
        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,父类加载器为commonLoader
        catalinaLoader = createClassLoader("server", commonLoader);
        // 创建sharedLoader,父类加载器为commonLoader
        sharedLoader = createClassLoader("shared", commonLoader);
    } catch (Throwable t) {
        // 之后

创建的过程中跳出异常了,日志记录完成前一天直接系统退出
        handleThrowable(t);
        log.error("Class loader creation threw exception", t);
        System.exit(1);
    }
}

所有的类加载器的创建都使用到了依据createClassLoader,全都,亲戚亲戚让我门让我门让我门进一步分析一下这人 依据。createClassLoader用到了CatalinaProperties.getProperty("xxx")依据,这人 依据用于从conf/catalina.properties文件获取属性值。

private ClassLoader createClassLoader(String name, ClassLoader parent)
    throws Exception {
    // 获取类加载器待加载的位置,之后

为空,则不还要加载特定的位置,使用父类加载返回回去。
    String value = CatalinaProperties.getProperty(name + ".loader");
    if ((value == null) || (value.equals("")))
        return parent;
    // 替换属性变量,比如:${catalina.base}、${catalina.home}
    value = replace(value);

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

   // 解析属性路径变量为仓库路径数组
    String[] repositoryPaths = getPaths(value);

    // 对每个仓库路径进行repositories设置。亲戚亲戚让我门让我门让我门还还要把repositories看成另一四个

个待加载的位置对象,还还之后

另一四个

classes目录,另一四个

jar文件目录等等
    for (String repository : repositoryPaths) {
        // Check for a JAR URL repository
        try {
            @SuppressWarnings("unused")
            URL url = new URL(repository);
            repositories.add(
                    new Repository(repository, RepositoryType.URL));
            continue;
        } catch (MalformedURLException e) {
            // Ignore
        }

        // Local repository
        if (repository.endsWith("*.jar")) {
            repository = repository.substring
                (0, repository.length() - "*.jar".length());
            repositories.add(
                    new Repository(repository, RepositoryType.GLOB));
        } else if (repository.endsWith(".jar")) {
            repositories.add(
                    new Repository(repository, RepositoryType.JAR));
        } else {
            repositories.add(
                    new Repository(repository, RepositoryType.DIR));
        }
    }
    // 使用类加载器工厂创建另一四个

类加载器
    return ClassLoaderFactory.createClassLoader(repositories, parent);
}

亲戚亲戚让我门让我门让我门来分析一下ClassLoaderFactory.createClassLoader--类加载器工厂创建类加载器。

public static ClassLoader createClassLoader(List<Repository> repositories,
                                            final ClassLoader parent)
    throws Exception {

    if (log.isDebugEnabled())
        log.debug("Creating new class loader");

    // Construct the "class path" for this class loader
    Set<URL> set = new LinkedHashSet<>();
    // 遍历repositories,对每个repository进行类型判断,并生成URL,每个URL亲戚亲戚当让我门让我门让我门会

校验其有效性,有效的URL亲戚亲戚让我门让我门让我门会倒进URL集合中
    if (repositories != null) {
        for (Repository repository : repositories)  {
            if (repository.getType() == RepositoryType.URL) {
                URL url = buildClassLoaderUrl(repository.getLocation());
                if (log.isDebugEnabled())
                    log.debug("  Including URL " + url);
                set.add(url);
            } else if (repository.getType() == RepositoryType.DIR) {
                File directory = new File(repository.getLocation());
                directory = directory.getCanonicalFile();
                if (!validateFile(directory, RepositoryType.DIR)) {
                    continue;
                }
                URL url = buildClassLoaderUrl(directory);
                if (log.isDebugEnabled())
                    log.debug("  Including directory " + url);
                set.add(url);
            } else if (repository.getType() == RepositoryType.JAR) {
                File file=new File(repository.getLocation());
                file = file.getCanonicalFile();
                if (!validateFile(file, RepositoryType.JAR)) {
                    continue;
                }
                URL url = buildClassLoaderUrl(file);
                if (log.isDebugEnabled())
                    log.debug("  Including jar file " + url);
                set.add(url);
            } else if (repository.getType() == RepositoryType.GLOB) {
                File directory=new File(repository.getLocation());
                directory = directory.getCanonicalFile();
                if (!validateFile(directory, RepositoryType.GLOB)) {
                    continue;
                }
                if (log.isDebugEnabled())
                    log.debug("  Including directory glob "
                        + directory.getAbsolutePath());
                String filenames[] = directory.list();
                if (filenames == null) {
                    continue;
                }
                for (int j = 0; j < filenames.length; j++) {
                    String filename = filenames[j].toLowerCase(Locale.ENGLISH);
                    if (!filename.endsWith(".jar"))
                        continue;
                    File file = new File(directory, filenames[j]);
                    file = file.getCanonicalFile();
                    if (!validateFile(file, RepositoryType.JAR)) {
                        continue;
                    }
                    if (log.isDebugEnabled())
                        log.debug("    Including glob jar file "
                            + file.getAbsolutePath());
                    URL url = buildClassLoaderUrl(file);
                    set.add(url);
                }
            }
        }
    }

    // Construct the class loader itself
    final URL[] array = set.toArray(new URL[set.size()]);
    if (log.isDebugEnabled())
        for (int i = 0; i < array.length; i++) {
            log.debug("  location " + i + " is " + array[i]);
        }

    // 从这儿看,最终所有的类加载器都会

URLClassLoader的对象~~
    return AccessController.doPrivileged(
            new PrivilegedAction<URLClassLoader>() {
                @Override
                public URLClassLoader run() {
                    if (parent == null)
                        return new URLClassLoader(array);
                    else
                        return new URLClassLoader(array, parent);
                }
            });
}

亲戚亲戚让我门让我门让我门之后 对initClassLoaders分析完了,接下来分析SecurityClassLoad.securityClassLoad,亲戚亲戚让我门让我门让我门看看顶端做了哪些事情

public static void securityClassLoad(ClassLoader loader) throws Exception {
    securityClassLoad(loader, true);
}

static void securityClassLoad(ClassLoader loader, boolean requireSecurityManager) throws Exception {

    if (requireSecurityManager && 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);
}

 private static final void loadCorePackage(ClassLoader loader) throws Exception {
    final String basePackage = "org.apache.catalina.core.";
    loader.loadClass(basePackage + "AccessLogAdapter");
    loader.loadClass(basePackage + "ApplicationContextFacade$PrivilegedExecuteMethod");
    loader.loadClass(basePackage + "ApplicationDispatcher$PrivilegedForward");
    loader.loadClass(basePackage + "ApplicationDispatcher$PrivilegedInclude");
    loader.loadClass(basePackage + "ApplicationPushBuilder");
    loader.loadClass(basePackage + "AsyncContextImpl");
    loader.loadClass(basePackage + "AsyncContextImpl$AsyncRunnable");
    loader.loadClass(basePackage + "AsyncContextImpl$DebugException");
    loader.loadClass(basePackage + "AsyncListenerWrapper");
    loader.loadClass(basePackage + "ContainerBase$PrivilegedAddChild");
    loadAnonymousInnerClasses(loader, basePackage + "DefaultInstanceManager");
    loader.loadClass(basePackage + "DefaultInstanceManager$AnnotationCacheEntry");
    loader.loadClass(basePackage + "DefaultInstanceManager$AnnotationCacheEntryType");
    loader.loadClass(basePackage + "ApplicationHttpRequest$AttributeNamesEnumerator");
}

这儿不言而喻全都使用catalinaLoader加载tomcat源代码顶端的各个专用类。亲戚亲戚让我门让我门让我门大致罗列一下待加载的类所在的package:

  1. org.apache.catalina.core.*
  2. org.apache.coyote.*
  3. org.apache.catalina.loader.*
  4. org.apache.catalina.realm.*
  5. org.apache.catalina.servlets.*
  6. org.apache.catalina.session.*
  7. org.apache.catalina.util.*
  8. org.apache.catalina.valves.*
  9. javax.servlet.http.Cookie
  10. org.apache.catalina.connector.*
  11. org.apache.tomcat.*

好了,至此亲戚亲戚让我门让我门让我门之后 分析完了init顶端涉及到的十几个 关键依据

WebApp类加载器

到这儿,亲戚亲戚让我门让我门让我门隐隐感觉到少分析了点哪些!没错,全都WebApp类加载器。整个启动过程分析下来,亲戚亲戚让我门让我门让我门仍然这么看得人这人 类加载器。它又是在哪儿跳出的呢?

亲戚亲戚让我门让我门让我门知道WebApp类加载器是Web应用私有的,而每个Web应用不言而喻有无另一四个 Context,这么亲戚亲戚让我门让我门让我门通过Context的实现类应该还还要发现。在Tomcat中,Context的默认实现为StandardContext,亲戚亲戚让我门让我门让我门看看这人 类的startInternal()依据,在这儿亲戚亲戚让我门让我门让我门发现了亲戚亲戚让我门让我门让我门感兴趣的WebApp类加载器。

protected synchronized void startInternal() throws LifecycleException {
    if (getLoader() == null) {
        WebappLoader webappLoader = new WebappLoader(getParentClassLoader());
        webappLoader.setDelegate(getDelegate());
        setLoader(webappLoader);
    }
}

入口代码非常简单,全都webappLoader不居于的前一天创建另一四个 ,并调用setLoader依据。

总结

亲戚亲戚让我门让我门让我门终于完正地分析完了Tomcat的整个启动过程+类加载过程。也了解并学习了Tomcat不同的类加载机制是为哪些要原先设计,带来的附加作用又是怎么能否的。