Tomcat Classloader

Tomcat classloader

tomcat类加载器结构

  • Bootstrap ClassLoader 启动类加载器
    加载Java核心库(JAVA_HOME/jre/lib)到JVM中.用c/c++实现.是JVM的一部分, JVM一启动就将指定的类加载到内存中,避免以后多次I/O.Java程序无法直接使用它

  • Extension ClassLoader 拓展类加载器
    加载Java拓展库(JAVA_HOME/jre/lib/ext).该类(ExtensionClassLoader)使用BootstrapClassLoader加载

  • SystemClassLoader 系统类加载器 (又名 程序类加载器 ApplicationClassLoader)
    加载用户类路径(CLASSPATH)指定的类库,若程序没有自己定义类加载器, 就默认使用 系统类加载器. 它也由BootstrapClassLoader加载,但它的父加载器被设置成了ExtensionClassLoader.
    它负责加载应用程序的类,包括自己写的和引入的第三方法类库,即所有在类路径中指定的类。
    负责加载用户类路径(-cp参数)指定的类库

  • Common ClassLoader
    commonClassLoader 负责加载 tomcat 应用服务器内部 和 web应用 均可见的类.
    如Servlet规范相关包 和 一些通用的工具包. tomcat应用服务器 顶层 公共类加载器,路径为 common.loader,默认指向$CATALINA_BASE/lib 和 $CATALINA_HOME/lib下的包.
    默认情况下 CommonClassLoader,CatalinaClassLoader,SharedClassLoader 是同一个, 均为common.但是可配置创建3个不同的类加载器,使他们各司其职

  • Catalina ClassLoader
    CatalinaClassLoader 加载 只有tomcat应用服务器内部 可见的类,这些类对web应用不可见.
    如tomcat的具体实现类 加载Tomcat应用服务的 类加载器 路径 server.loader,默认为空. 此时tomcat使用commonClassLoader作为web应用的父加载器

  • Shared ClassLoader
    SharedClassLoader 负责加载web应用共享的类,这些类tomcat不会依赖.
    是所有web应用的父加载器,路径为 shared.loader,默认为空. 此时tomcat使用CommonLoader作为 web应用的父加载器

  • WebApp1 ClassLoader
    加载/WEB-INF/classes目录下的未压缩的Class和资源文件,以及/WEB-INF/lib下的Jar包.该类加载器 只对当前Web应用可见, 对其他web应用均不可见.
    Tomcat7 默认使用 WebappClassLoader 类加载器. 而 Tomcat8 默认使用 ParallelWebappClassLoader,支持并行加载类的特性.

  • WebApp2 ClassLoader
    其它web应用类加载器…

创建 CommonClassLoader

Bootstrap.init() 时会实例化类加载器.
在初始化类加载器之后立即 设置 线程上下文类加载器(Thread Context ClassLoader)为 catalina 类加载器
为 Catalina 组件指定父类加载器

org.apache.catalina.startup.Bootstrap#initClassLoaders

/** 初始化类加载器 */
private void initClassLoaders() {
    try {
        commonLoader = createClassLoader("common", null); // 创建一个 Common 类加载器
        if( commonLoader == null ) {
            // no config file, default to this loader - we might be in a 'single' env.
            commonLoader=this.getClass().getClassLoader();
        }// 默认catalinaLoader和sharedLoader都使用父类加载器commonLoader
        catalinaLoader = createClassLoader("server", commonLoader); // 再根据 Common 类加载器创建 server 类加载器
        sharedLoader = createClassLoader("shared", commonLoader); // 再根据 Common 类加载器创建 shared 类加载器
    } catch (Throwable t) {
        handleThrowable(t);
        log.error("Class loader creation threw exception", t);
        System.exit(1);
    }
}

WebappClassLoaderBase 继承结构

ClassLoader (java.lang)
    SecureClassLoader (java.security)
        URLClassLoader (java.net)
            WebappClassLoaderBase (org.apache.catalina.loader)
                ParallelWebappClassLoader (org.apache.catalina.loader)
                WebappClassLoader (org.apache.catalina.loader)

WebappLoader

WebappClassLoaderBase 实现了主要的逻辑,并且继承了 Lifecycle,在 tomcat 组件启动、关闭时会完成资源的加载、卸载操作.
例如在 start 过程会读取 /WEB-INF/classes/WEB-INF/lib 资源,并且记录每个 jar 包的时间戳方便重载 jar 包.
在组件 stop 的时候,会清理已经加载的资源;
destory 时会显式地触发 URLClassLoader.close()

WebappLoader 持有 WebappClassLoaderBase.
WebappClassLoaderBase 的具体实现包括 ParallelWebappClassLoader 和 WebappClassLoader.

初始化 WebappLoader

在 Tomcat 中,每个 webapp 对应一个 StandardContext,在 start 过程便会实例化 WebappLoader,并且调用其 start() 方法完成初始化,包括创建 ParallelWebappClassLoader 实例,然后,还会启动 Context 的子容器。
这两个过程,都会将线程上下文类加载器指定为 ParallelWebappClassLoader 类加载器,在完成 webapp 相关的类加载之后,又将线程上下文类加载器设置为 catalina 类加载器。

StandardContext 启动时 会 实例化 WebappLoader

ClassLoader oldCCL = bindThread();

if (getLoader() == null) { 
    // 创建 WebappLoader,每个web应用都使用各自的 WebappClassLoader
    WebappLoader webappLoader = new WebappLoader(getParentClassLoader());
    webappLoader.setDelegate(getDelegate());
    setLoader(webappLoader);
}
// ...
unbindThread(oldCCL);

// Start our subordinate components, if any
// 启动 WebappLoader
Loader loader = getLoaderInternal();
if ((loader != null) && (loader instanceof Lifecycle))
    ((Lifecycle) loader).start();// 启动web应用类加载器(WebappLoader)

WebappLoader 用于管理 WebappClassLoader

WebappLoader 在 startInternal() 中 创建 WebappClassLoader

WebappLoader 提供 backgroundProcess() 用于 Context 后台处理.当检测 web 应用的类文件,jar 包发生变更时,重新加载 Context.

WebappLoader 在 stop 时,会 销毁 WebappClassLoader

tomcat类加载顺序

  • 默认加载顺序,delegate=false 即不启用java委派模式

    • 1.从缓存中加载
    • 2.若无,则从 BootstrapClassLoader 加载(保证Java核心类库不会被覆盖)
    • 3.若无,则从当前类加载器加载(即 WebAppClassLoader,按照 WEB-INF/classes, WEB-INF/lib 顺序)
    • 4.若无,则从父类加载器加载,由于父类加载器 采用默认的委派机制,则加载顺序为 System -> Common -> Shared (从父到子)
  • 配置 delegate=true 即启用java委派模式

    • 1.从缓存中加载
    • 2.若无,则从 BootstrapClassLoader 加载
    • 3.若无,则从父类加载器加载,加载顺序为 System -> Common -> Shared (从父到子)
    • 4.若无,则从当前类加载器加载(即 WebAppClassLoader,按照 WEB-INF/classes, WEB-INF/lib 顺序)
  • JVM endorsed机制

  • 定义

    • Endorsed Standards Override Mechanism 机制用于 允许替换JCP之外生成的api,该机制允许应用程序 提供新版本的api来覆盖JVM的默认实现.
      默认目录 %JAVA_HOME%/lib/endorsed,可通过启动参数java.endorsed.dir来修改.该目录下的jar包优先级高于jvm中的类加载.(但是规定只有部分类库被允许替换)
  • 实例

    • web应用通过endorsed加载新的JAXP包,甚至核心类库(rt.jar)

逻辑再 org.apache.catalina.loader.WebappClassLoaderBase#loadClass(java.lang.String, boolean) 里

// (0)Check our previously loaded local class cache.检查 WebappClassLoader 缓存中是否加载过此类. resourceEntries
clazz = findLoadedClass0(name);

// (0.1) Check our previously loaded class cache 2.检查 JVM 虚拟机中是否加载过该类.调用 java.lang.ClassLoader 中的 native 方法
clazz = findLoadedClass(name);

// (0.2) Try loading the class with the system class loader, to prevent the webapp from overriding J2SE classes
// 使用 ExtClassLoader 加载该类(当然它也会委托它的父类加载器 BootstrapClassLoader),防止覆盖 J2SE 类库
clazz = j2seClassLoader.loadClass(name);

// (0.5) Permission to access this class when using a SecurityManager

// (1) Delegate to our parent if requested. 
// 若 delegate 设置为 true,则会完全按照 JVM 的双亲委托机制流程加载类.委托给parent,即 CommonClassLoader,它会继续向上委托.
if (delegateLoad) {
    // parent为 CommonClassLoader
    clazz = Class.forName(name, false, parent);
}

// (2) Search local repositories
// 使用 WebappClassLoader.findClass() 加载类
clazz = findClass(name); 
    clazz = findClassInternal(name);
        entry = findResourceInternal(name, path, true); // 1.通过名称去当前 webappClassLoader 的仓库中查找对应的类文件
            clazz = defineClass() // 2.将找到的类文件通过 defineClass() 转变为 Jvm 可以识别的 Class 对象返回

// (3) Delegate to parent unconditionally 6.若还是没找到,则委托给父类加载器加载
clazz = Class.forName(name, false, parent);

热加载

当 context 配置了 reloadable="true",WebappLoader 会启动 backgroundProcess(),它用于监听 class、jar 等文件的变更,一旦有变动,便会对 Context 容器进行 reload 操作.

通过 WebappLoader#setContext(Context context) 方法调用,从而传递给 WebappLoader.

<Context path="examples" docBase="/tomcat/webapps/examples" reloadable="true" />

org.apache.catalina.loader.WebappLoader#backgroundProcess

public void backgroundProcess() {
        if (reloadable && modified()) {
            try {
                Thread.currentThread().setContextClassLoader
                    (WebappLoader.class.getClassLoader()); // 变更线程上下文类加载器为 webapp 类加载器
                if (container instanceof StandardContext) {
                    ((StandardContext) container).reload(); // 重载 webapp
                }
            } finally {
                if (container.getLoader() != null) {
                    Thread.currentThread().setContextClassLoader
                        (container.getLoader().getClassLoader());
                }
            }
        } else {
            closeJARs(false);
        }
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FlyingZCC

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值