ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

打破双亲委派的两种场景

2022-04-26 23:33:19  阅读:427  来源: 互联网

标签:委派 场景 Java Tomcat ClassLoader 双亲 加载



theme: smartblue
highlight: androidstudio

  本文已参与「新人创作礼」活动, 一起开启掘金创作之路。

前言

  昨天的文章JVM学习笔记P2—Java中的类加载器介绍了Java中的类加载机制,里面提到了双亲委派机制可以保证Java的运行安全,保证Java中的核心类可以被正确安全加载。那有没有打破双亲委派机制的Java应用呢?其实是有的,比如运行Java Web应用的Tomcat容器以及数据库操作中间件JDBC,它们就都打破了双亲委派机制了。为什么它们如此特殊呢?

Tomcat打破双亲委派

  通常使用Java开发Web应用时,最后我们都会将源码打包成一个war包或者jar包,然后扔进Tomcat容器里去运行。通常情况下,一台服务器上只会部署一个Tomcat容器,现在因为公司资源有限,我想在Tomcat里部署多个应用可不可以?肯定是可以的,只要server.port设置不同,就可以在Tomcat里部署多个应用。那现在问题来了,不同应用里可能使用了同一个jar包的不同版本,就比如常见的lombok,就有很多个不同的版本。如果Tomcat还是使用Java默认的双亲委派机制(注:Tomcat也是Java写的),那就只会加载一个lombok?那Tomcat到底加载哪个?还有就是如mysql-connector-java这种比较特殊的,如果mysql是5.7版本的,只能使用mysql-connector-java的特定版本来加载。此时如果Tomcat还是遵从双亲委派就无法支持这种场景

Tomcat的类加载机制

image.png

上图就是Tomcat中的类加载机制。可以看到,最顶层的三个类加载器还是Java中默认的类加载器。Common ClassLoaderCatalina ClassLoaderShared ClassLoaderWebapp ClassLoaderJasper Loader则是Tomcat内部自定义的类加载器。

  • Common ClassLoader加载的类都可以被Catalina ClassLoaderShare ClassLoader使用,从而实现公有类库的公用

  • Catalina ClassLoaderShare ClassLoader各自加载的类则与对方相互隔离,实现了Tomcat自身依赖与Web应用依赖的隔离

  • WebApp ClassLoader加载每个应用/WebApp/WEB-INF/*目录下的资源,WebApp ClassLoader可以使用Share ClassLoader加载的类,但各个
    WebApp ClassLoader之间相互隔离

  • Jasper Loader主要是用来加载JSP文件的。JSP其实也是Java文件,其编译后也会生成一个.class文件

  • Jasper Loader的加载范围仅仅是这个JSP文件所编译出来的那个.class文件,一对一的设计是为了随时丢弃它,以实现热加载功能

  • Tomcat会在后台启动一个线程来监听JSP文件变化,如果这个JSP文件变化了则找到这个JSP对应的Servlet的类加载器对象引用,重新生成新的Jasper Loader类加载器对象赋值,然后加载新的JSP对应的Servlet类,原先的那个类加载器对象会在下次GC被回收

      综上,Tomcat打破了双亲委派机制让每个WebApp ClassLoader加载自己目录下的class文件没有向上传递给父加载器Tomcat通过这样一套类加载机制,实现了各个Web应用之间,相同依赖版本的统一加载,不同依赖版本的分别加载以及随时热加载JSP文件的功能。

JDBC打破双亲委派

  Java开发难免少不了与数据库打交道。而JDBCJava中与数据库连接的APIJDBCDriver是定义在rt.jar包中的,但是它的实现通常是由不同的数据库厂商来完成的,如mysql-connetor-java。同时rt.jar包中的DriverManager类会加载每个Driver接口的实现类并管理它们。那么问题来了,根据类加载机制,某个类需要引用其它类的时候,虚拟机将会用这个类的classloader去加载被引用的类。既然DriverManager是由BootStrap ClassLoader加载,那么各个Driver的实现类理应也由BootStrap ClassLoader来加载。但很显然,Boostrap Classloader显然是无法加载到mysql-connetor-java的,二者明显不在一个目录里,直接报ClassNotFound。因此只能在DriverManager里强行指定下层的classloader来加载Driver实现类,而这就会打破双亲委派模型

JDBC实现方式

  在JDBC 4.0之后,借助于JavaSPI机制,可以实现自动加载。通过查看DriverManager类的代码可以看到,当我们使用DriverManager的时候就会触发static代码块,进而会加载META-INF/services/java.sql.Driver指定的类。

static {
    loadInitialDrivers();
    println("JDBC DriverManager initialized");
}

private static void loadInitialDrivers() {
    AccessController.doPrivileged(new PrivilegedAction<Void>() { // 1. AccessController,Java安全模型
        public Void run() {
            ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class); // 2. 核心,ServiceLoader就是JDK提供的SPI的实现方式
            Iterator<Driver> driversIterator = loadedDrivers.iterator();
            try{
                while(driversIterator.hasNext()) {
                    driversIterator.next(); // 3. 遍历的过程会触发每个Driver实现类的加载
                }
            } catch(Throwable t) {
              // Do nothing
            }
            return null;
        }
    });
}

  ServiceLoader#load方法中,通过获取Thread.currentThread()context class loader来根据配置加载对应的类。

public static <S> ServiceLoader<S> load(Class<S> service) {

    // Thread.currentThread().getContextClassLoader() 默认是系统类加载器
    ClassLoader cl = Thread.currentThread().getContextClassLoader();
    return ServiceLoader.load(service, cl);
}

  Thread.currentThread().getContextClassLoader()默认返回应用类加载器,这个可以从sun.misc.Launch类中看到。

public class Launcher {

public Launcher() {
    ...
    try {
                               //应用类加载器
        this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
    } catch (IOException var9) {
        throw new InternalError("Could not create application class loader", var9);
    }
    Thread.currentThread().setContextClassLoader(this.loader);
    ...
  }


}

总结

  分析了两种打破Java双亲委派的情况,希望大家看完总结消化一下。

标签:委派,场景,Java,Tomcat,ClassLoader,双亲,加载
来源: https://www.cnblogs.com/reecelin/p/16197167.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有