Tomcat源码分析 (五)

  • 时间:
  • 浏览:0
  • 来源:大发快三_快三破解_大发快三破解

在研究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不同的类加载机制是为有哪些要原先设计,带来的附加作用又是如可的。