破坏双亲委派模型

破坏双亲委派模型有两种方式

一、引入线程上下文类加载器

Java 提供了很多服务提供者接口(Service Provider Interface,SPI),允许第三方为这些接口提供实现。常见的 SPI 有 JDBC、JCE、JNDI、JAXP 和 JBI 等。
这些 SPI 的接口由 Java 核心库来提供,而这些 SPI 的实现代码则是作为 Java 应用所依赖的 jar 包被包含进类路径(CLASSPATH)里。SPI接口中的代码经常需要加载具体的实现类。那么问题来了,SPI的接口是Java核心库的一部分,是由引导类加载器来加载的;SPI的实现类是由系统类加载器来加载的。引导类加载器是无法找到 SPI 的实现类的,因为依照双亲委派模型,BootstrapClassloader无法委派AppClassLoader来加载类。
而线程上下文类加载器破坏了双亲委派模型,可以在执行线程中抛弃双亲委派加载链模式,使程序可以逆向使用类加载器。

刚开始有个疑问就是:既然你是SPI接口,那你提供接口就好了,为什么要加载具体的实现类?

后来想想SLF4J和LOG4也确实是这样,我们项目中操作LOG4J也是统一使用的SLF4J提供的接口LoggerFactory.getLogger,事实上LoggerFactory也需要引入SLF4J-LOG4J.jar包中的StaticLoggerBinder来提供统一操作接口。由于SLF4J和LOG4都是第三方类库,双方可以互相随意的引用,但是某些SPI 的接口由Java核心库提供,他们没法直接引用第三方库。

后来发现JDK中确实有一些这种情况:
1、可能接口也想提供一个默认的实现。如 JAXP 中的 javax.xml.parsers.DocumentBuilderFactory类中的 newInstance()方法用来生成一个新的 DocumentBuilderFactory的实例。这里的实例的真正的类是继承自 javax.xml.parsers.DocumentBuilderFactory,由 SPI 的实现所提供的。如在 Apache Xerces 中,实现的类是 org.apache.xerces.jaxp.DocumentBuilderFactoryImpl。而问题在于,SPI 的接口是 Java 核心库的一部分,是由引导类加载器来加载的;SPI 实现的 Java 类一般是由系统类加载器来加载的。引导类加载器是无法找到 SPI 的实现类的,因为它只加载 Java 的核心库。它也不能代理给系统类加载器,因为它是系统类加载器的祖先类加载器。


破坏双亲委派模型_第1张图片
image.png

image.png

可以发现DocumentBuilderFactoryImpl本来是存在于org.apache.xerces.jaxp这个包里的,是第三方类库,使用的加载器是AppClassLoader,后来JDK8把他移到了rt.jar内,应该也是为了不破坏双亲委派模型。

2、JDBC java.sql.DriverManager统一注册实现类和获取连接


破坏双亲委派模型_第2张图片
image.png
破坏双亲委派模型_第3张图片
image.png
    //  Worker method called by the public getConnection() methods.
    private static Connection getConnection(
        String url, java.util.Properties info, Class caller) throws SQLException {
        /*
         * When callerCl is null, we should check the application's
         * (which is invoking this class indirectly)
         * classloader, so that the JDBC driver class outside rt.jar
         * can be loaded from here.
         */
        ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
        synchronized(DriverManager.class) {
            // synchronize loading of the correct classloader.
            if (callerCL == null) {
                callerCL = Thread.currentThread().getContextClassLoader();
            }
        }

        if(url == null) {
            throw new SQLException("The url cannot be null", "08001");
        }

        println("DriverManager.getConnection(\"" + url + "\")");

        // Walk through the loaded registeredDrivers attempting to make a connection.
        // Remember the first exception that gets raised so we can reraise it.
        SQLException reason = null;

        for(DriverInfo aDriver : registeredDrivers) {
            // If the caller does not have permission to load the driver then
            // skip it.
            if(isDriverAllowed(aDriver.driver, callerCL)) {
                try {
                    println("    trying " + aDriver.driver.getClass().getName());
                    Connection con = aDriver.driver.connect(url, info);
                    if (con != null) {
                        // Success!
                        println("getConnection returning " + aDriver.driver.getClass().getName());
                        return (con);
                    }
                } catch (SQLException ex) {
                    if (reason == null) {
                        reason = ex;
                    }
                }

            } else {
                println("    skipping: " + aDriver.getClass().getName());
            }

        }

        // if we got here nobody could connect.
        if (reason != null)    {
            println("getConnection failed: " + reason);
            throw reason;
        }

        println("getConnection: no suitable driver found for "+ url);
        throw new SQLException("No suitable driver found for "+ url, "08001");
    }

java.sql.DriverManager也是通过启动类加载加载的核心类库,但是当引入具体实现类(这里是MYSQL),DriverManager也需要将具体实现类注册到自身,所以也是需要在核心库类中引入第三方库类。
在上面引用的DriverManager.getConnection方法中也可以看到实际上也用到了Thread.currentThread().getContextClassLoader()方法来引入线程上下文类加载器加载MYSQL。

二、自定义ClassLoader

  1. 如果不想不破坏双亲委派模型,只要去重写findClass方法
  2. 如果想要去破坏双亲委派模型,需要去重写loadClass方法

破坏双亲委派模型和自定义自己的类加载器

你可能感兴趣的:(破坏双亲委派模型)