Tomcat是一个经典的web server,学习tomcat的源码对于我们是有很大的帮助的。前一段时间了解了tomcat的工作的大致流程,对我的新工作有了很大的帮助。刚学习了ClassLoader(学习classloader的初衷源于公司产品的一个bug),也将我对classloaderp写成了一篇博客。为了对ClassLoader有更多的理解,现在就来看看Tomcat 6 的ClassLoader设计。

之前通过对tomcat的启动过程、tomcat处理request的过程进行简单的了解,了解tomcat的各个组件及其功能。在这两个过程中,都有ClassLoader的影子,所以今天依旧从这两个方面入手,来了解Tomcat 如何使用ClassLoader。

StandardClassLoader

Bootstrap.main()方法简单点说就是执行4个方法:init , setAwait, load, start。

init

public void init()
throws Exception
{ // Set Catalina path
setCatalinaHome();
setCatalinaBase(); initClassLoaders(); Thread.currentThread().setContextClassLoader(catalinaLoader); SecurityClassLoad.securityClassLoad(catalinaLoader); // Load our startup class and call its process() method
if (log.isDebugEnabled())
log.debug("Loading startup class");
Class startupClass =
catalinaLoader.loadClass
("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.newInstance(); // Set the shared extensions class loader
if (log.isDebugEnabled())
log.debug("Setting startup class properties");
String methodName = "setParentClassLoader";
Class paramTypes[] = new Class[1];
paramTypes[0] = Class.forName("java.lang.ClassLoader");
Object paramValues[] = new Object[1];
paramValues[0] = sharedLoader;
Method method =
startupInstance.getClass().getMethod(methodName, paramTypes);
method.invoke(startupInstance, paramValues); catalinaDaemon = startupInstance; }

在init方法中调用首先initClassLoader方法来初始化Tomcat的ClassLoader模块,然后是使用刚自定义类加载器加载catalinaLoader 来加载org.apache.catalina.startup.Catalina 类。接下来是调用Catalina中的setParentClassLoader方法。

那就看看initClassLoader中将Tomcat的ClassLoader模块初始化成什么样子的:

private void initClassLoaders() {
try {
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 = createClassLoader("server", commonLoader);
sharedLoader = createClassLoader("shared", commonLoader);
} catch (Throwable t) {
log.error("Class loader creation threw exception", t);
System.exit(1);
}
}

这里其实就是创建了3个ClassLoader,分别是commonLoader, catalinaLoader, sharedLoader。而其实它们之间是有这某种关系的:

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

也就是说启动过程初始化后的ClassLoader模型为:

其中顶部的3个classLoader对象都由JDK提供的。commonLoader, catalinaLoader, sharedLoader它们三个有一个共同的名字:StandardClassLoader。

bootstrapClassLoader是加载java_home/jre/lib目录下的个别jar包(不是全部)

extClassLoader是加载java_home/jre/lib/ext目录下的jar包

AppClassLoader是加载classpath中指定的jar包

在catalina.properties文件中则指出了这3个ClassLoader默认的加载路径。

common.loader=${catalina.home}/lib,${catalina.home}/lib/*.jar
server.loader=
shared.loader=

这个文件是在jar包中的,在使用tomcat时是不可以修改的。如果想要修改默认的加载路径,该怎么办呢?可以在catalina.config配置

想要了解是什么原因,可以参考CatalinaProperties类的实现。

上面说了Bootstrap的main方法中其实就是调用了4个方法init,setAwait, start, load,除了init外,另外3个方法其实就是调用Catalina对象的对应的setAwait,start, load方法。

从init方法的实现中,可以知道,在Bootstrap类中,除了Catalina类是由catalinaClassLoader加载的之外,其余的类都是JDK提供的ClassLoader加载的。也就是说根据上节学习的内容,Catalina类在当前类Bootstrap类中是不能直接调用的。然而这里又要调用Catalina中的方法,我在上节的测试中,采取的是另外启动一个线程来解决的。今天就来看看Tomcat中如何解决这样的应用场景的:

1)init中调用Catalina的setParentClassLoader方法

Object startupInstance = startupClass.newInstance();

        // Set the shared extensions class loader
if (log.isDebugEnabled())
log.debug("Setting startup class properties");
String methodName = "setParentClassLoader";
Class paramTypes[] = new Class[1];
paramTypes[0] = Class.forName("java.lang.ClassLoader");
Object paramValues[] = new Object[1];
paramValues[0] = sharedLoader;
Method method =
startupInstance.getClass().getMethod(methodName, paramTypes);
method.invoke(startupInstance, paramValues);

2) setAwait中调用catalina的setAwait方法

public void setAwait(boolean await)
throws Exception { Class paramTypes[] = new Class[1];
paramTypes[0] = Boolean.TYPE;
Object paramValues[] = new Object[1];
paramValues[0] = new Boolean(await);
Method method =
catalinaDaemon.getClass().getMethod("setAwait", paramTypes);
method.invoke(catalinaDaemon, paramValues); }

3)start调用catalina的start方法

public void start()
throws Exception {
if( catalinaDaemon==null ) init(); Method method = catalinaDaemon.getClass().getMethod("start", (Class [] )null);
method.invoke(catalinaDaemon, (Object [])null); }

4)load调用catalina的load方法

private void load(String[] arguments)
throws Exception { // Call the load() method
String methodName = "load";
Object param[];
Class paramTypes[];
if (arguments==null || arguments.length==0) {
paramTypes = null;
param = null;
} else {
paramTypes = new Class[1];
paramTypes[0] = arguments.getClass();
param = new Object[1];
param[0] = arguments;
}
Method method =
catalinaDaemon.getClass().getMethod(methodName, paramTypes);
if (log.isDebugEnabled())
log.debug("Calling startup class " + method);
method.invoke(catalinaDaemon, param); }

它们都是使用反射来处理。

现在看来,要解决由不同classLoader加载器加载的类之前的方法调用,就有2种处理方案了:

方案一:另启动一个线程

方案二:使用反射。

Catalina是由catalinaClassLoader加载的,Tomcat启动过程会将Tomcat的相关组件加载并初始化,而这些过程的入口都在Catalina中。所以呢Tomcat中绝在部分组件应当都是由catalinaClassLoader加载的。这句话说的可能有些满,不过呢,我这么说也是有原因的:

在init方法中有这么一个过程:SecurityClassLoad.securityClassLoad(catalinaLoader);

下面来看看这个过程的真面目:

public static void securityClassLoad(ClassLoader loader)
throws Exception { if( System.getSecurityManager() == null ){
return;
} loadCorePackage(loader);
loadLoaderPackage(loader);
loadServletsPackage(loader);
loadSessionPackage(loader);
loadUtilPackage(loader);
loadJavaxPackage(loader);
loadCoyotePackage(loader);
loadHttp11Package(loader);
loadTomcatPackage(loader);
} private final static void loadCorePackage(ClassLoader loader)
throws Exception {
String basePackage = "org.apache.catalina.";
loader.loadClass
(basePackage +
"core.ApplicationContextFacade$1");
loader.loadClass
(basePackage +
"core.ApplicationDispatcher$PrivilegedForward");
loader.loadClass
(basePackage +
"core.ApplicationDispatcher$PrivilegedInclude");
loader.loadClass
(basePackage +
"core.ContainerBase$PrivilegedAddChild");
loader.loadClass
(basePackage +
"core.StandardWrapper$1");
loader.loadClass
(basePackage +
"core.ApplicationHttpRequest$AttributeNamesEnumerator");
}

securityClassLoad(ClassLoader loader)采用了门面(facade)模式来加载各个类。这里只列出了loadCorePackage的实现。其它方法的实现与这个是完全一样的。所以上面 我才说Tomcat中绝在部分组件应当都是由catalinaClassLoader加载的。

同时根据这个方法,也可以看出,要加载内部类,要用外部类与内部类类名之间加上$

===============================================================================

WebappClassLoader

上面说的其实就是Tomcat中的StandardClassLoader,Tomcat中还有一种WebappClassLoader。根据名字就可以看出来,这个ClassLoader是用于加载各个Web Application而设计的。Tomcat的各个组件中与Web Application有对应关系的,也就是StandardContext。为何这么说呢?我们在Web应用中使用的ServletConext(Java EE的标准API)在Tomcat中由ApplicationContext来实现,而ApplicationContext其实就是StandardContext的委托。

这个Classloader是用于加载web应用程序中的类。这个类以后会有专门了解一下。

Tomcat源码解读:ClassLoader的设计的更多相关文章

  1. Tomcat源码解读系列(一)——server.xml文件的配置

    Tomcat是J2EE开发人员最常用到的开发工具,在Java Web应用的调试开发和实际部署中,我们都可以看到Tomcat的影子.大多数时候,我们可以将Tomcat当做一个黑盒来看待,只需要将编写的J ...

  2. SpringBoot启动tomcat源码解读

    一.SpringBoot自动拉起Tomcat 原文链接:http://www.studyshare.cn/blog-front/blog/details/1136 SpringBoot框架是当前比较流 ...

  3. SpringBoot启动嵌入式tomcat源码解读

    一.SpringBoot自动拉起Tomcat SpringBoot框架是当前比较流行的java后端开发框架,与maven结合大大简化了开发人员项目搭建的步骤,我们知道SpringBoot的启动类启动后 ...

  4. tomcat源码解读(1)–tomcat热部署实现原理

    tomcat的热部署实现原理:tomcat启动的时候会有启动一个线程每隔一段时间会去判断应用中加载的类是否发生变法(类总数的变化,类的修改),如果发生了变化就会把应用的启动的线程停止掉,清除引用,并且 ...

  5. 源码解读Dubbo分层设计思想

    一.Dubbo分层整体设计概述 我们先从下图开始简单介绍Dubbo分层设计概念: (引用自Duboo开发指南-框架设计文档) 如图描述Dubbo实现的RPC整体分10层:service.config. ...

  6. tomcat源码解读(2)–容器责任链模式的实现

    责任链模式:责任链模式可以用在这样的场景,当一个request过来的时候,需要对这个request做一系列的加工,使用责任链模式可以使每个加工组件化,减少耦合.也可以使用在当一个request过来的时 ...

  7. Spring-IOC源码解读1-整体设计

    1. SpringIOC提供了一个基本的javabean容器,通过IOC模式管理依赖关系,并通过依赖注入和AOP增强了为javabean这样的pojo对象赋予事务管理,生命周期管理等基本功能.2. S ...

  8. Tomcat源码分析

    前言: 本文是我阅读了TOMCAT源码后的一些心得. 主要是讲解TOMCAT的系统框架, 以及启动流程.若有错漏之处,敬请批评指教! 建议: 毕竟TOMCAT的框架还是比较复杂的, 单是从文字上理解, ...

  9. 【Tomcat源码学习】-1.概述

    Tomcat是用java语言开发的一个Web服务器,最近花了差不多两周时间对Tomcat 9.0源码进行了一遍学习,由于知识储备有限,也只是理解了一个大概,下面就由我来给大家分享一下我对Tomcat的 ...

随机推荐

  1. 炉石传说 C# 设计文档(序)

    经过3个月的开发,有很多感触. 以前一直以为技术是开发成败的第一因素,现在发现,等到你代码写的时间够长,经验够丰富,什么功能都能随手完成,对于业务的分析能力变成了第一位. 炉石山寨版的BS版本用到的H ...

  2. Android录音应用

    首先是xml布局文件: <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xm ...

  3. GET DIAGNOSTICS Syntax

    http://dev.mysql.com/doc/refman/5.7/en/get-diagnostics.html GET [CURRENT | STACKED] DIAGNOSTICS { st ...

  4. 标签栏使用Demo二

    // //  PHTagViewFrame.m //  标签的使用二 // //  Created by 123 on 16/9/6. //  Copyright © 2016年 彭洪. All ri ...

  5. [python拾遗]文件操作

    文件操作 1.open()函数 open()函数主要用于文件处理,一般分为下面3个过程: 1.打开文件 2.操作文件 3.关闭文件 常见的格式示例: f = open('note.txt','r') ...

  6. Python的sorted函数应用

    sorted()函数也是一个高阶函数,它还可以接收一个key函数来实现自定义的排序 L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88) ...

  7. ArcGIS Server开发实践之【Search Widget工具查询本地地图服务】

    加载本地地图服务,并实现要素的查询.(不足之处还请指点)具体代码如下: <!DOCTYPE html> <html dir="ltr"> <head& ...

  8. 分享5种风格的 jQuery 分页效果【附代码】

    jPaginate 是一款非常精致的分页插件,提供了五种不同风格的分页效果,支持鼠标悬停翻页,快速分页功能.这款插件还提供了丰富的配置选项,你可以根据需要进行设置. 效果演示      源码下载 各个 ...

  9. Ubuntu 各版本代号简介

    起名字是件伤脑筋的事,但是程序猿们似乎最喜欢干伤脑筋的活.Android 的每个版本都有个甜点的别名,而 Ubuntu ,每个版本都有一个更为特色的名字,这个名字由一个形容词和一个动物名称组成,并且, ...

  10. C#仿google日历asp.net简单三层版本

    网上搜了很多xgcalendar的例子都是Php开发的,而且官方站上的asp.net/MVC版 在vs10 08 都报错. 所以自己重新用三层写了一下希望对大家有帮助 废话不多说了 先看看它都有些什么 ...