打破双亲委派的几种办法

打破双亲委派的几种办法

  • Head
    • PandoraBoot
    • Tomcat
    • JDBC

Head

比较两个类是否“相等”,前提是这两个类由同一个类加载器加载,
否则,即使这两个类来源于同一个Class 文件,被同一个虚拟机加载,
只要加载它们的类加载器不同,那么这两个类就必定不相等。

PandoraBoot

Pandora Boot = Pandora + SpringBoot
由于内部二方包,中间件过多,全部是自研框架,会导致各种maven冲突,所以需要一个管理工具,
pandora主要解决了对大量二方包的依赖冲突问题

maven依赖
对于原始maven控制的pom文件中产生依赖冲突maven使用 路径最近+最先声明

但是maven解决冲突仍有缺点:
● 手动排除包只能规避风险,无法解决(exclusions ● 对于缺少高版本包的可能有 NoClassDefFoundError ClassNotFoundException
● 对于缺少低版本包方法or删除方法后的可能有 NoSuchMethodError

Pandora
源码就不仔细写了,大概流程是pandora通过不同的类加载器打破双亲委派,通过加载全类名@不同classloader名产生不同的类来解决问题。
源码最后有一个有意思的点是:重新开启一个线程执行Main方法启动Springboot,个人理解是之前的初始化线程中有太多的中间过程对象,exit掉本线程防止内存泄漏

Tomcat


tomcat对于不同应用需要有不同的隔离环境。
tomcat给每个应用都创建了一个WebApp ClassLoader类加载器
重写了load方法:不再向上查找,而是在本类查找不到后再向上。对于其他的需要共享的例如Redis,可以在上层Share ClassLoader中共享。

JDBC

原本的JDBC: Class.forName(“DriverName”) 是通过调用Driver中静态代码块中的

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    public Driver() throws SQLException {
    }

    static {
        try {
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}

将Driver注册

使用SPI的JDBC:
在mysql的jar包中的META-INF/services/java.sql.Driver 文件中指明当前使用的Driver,然后可以直接调用

 Connection conn= DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb?characterEncoding=GBK", "root", "");

问题是 :一个类的加载器和调用他的加载器相同
这里调用的是 bootstrap类加载器,无法加载到子类厂商中的类
使用了线程上下文加载器

public class DriverManager {
    static {
        loadInitialDrivers();
        println("JDBC DriverManager initialized");
    }
    private static void loadInitialDrivers() {
        //省略代码
        //这里就是查找各个sql厂商在自己的jar包中通过spi注册的驱动
        ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
        Iterator<Driver> driversIterator = loadedDrivers.iterator();
        try{
             while(driversIterator.hasNext()) {
                driversIterator.next();
             }
        } catch(Throwable t) {
                // Do nothing
        }

        //省略代码
    }
}
    public static <S> ServiceLoader<S> load(Class<S> service) {
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        return ServiceLoader.load(service, cl);
    }
    public static <S> ServiceLoader<S> load(Class<S> service,
                                            ClassLoader loader){
        return new ServiceLoader<>(service, loader);
    }

这个时候我们再看下整个mysql的驱动加载过程:

第一,获取线程上下文类加载器,从而也就获得了应用程序类加载器(也可能是自定义的类加载器)
第二,从META-INF/services/java.sql.Driver文件中获取具体的实现类名“com.mysql.jdbc.Driver”
第三,通过线程上下文类加载器去加载这个Driver类,从而避开了双亲委派模型的弊端

https://www.jianshu.com/p/09f73af48a98
https://www.zhihu.com/question/466696410

你可能感兴趣的:(java)