自定义类加载器

一个 Tomcat 程序中是可以运行多个 Web 应用的,如果这两个应用中出现了相同限定名的类,比如 Servlet 类,Tomcat 要保证这两个类都能加载并且它们应该是不同的类。

如果不打破双亲委派机制,当应用类加载器加载 Web 应用 1 中的 MyServlet 之后,Web 应用 2 中相同限定名的 MyServlet 类就无法被加载了。

Untitled

Tomcat 使用了自定义类加载器来实现应用之间类的隔离,每个应用都会有一个独立的类加载器加载对应的类。

Untitled

实际上,我们不应该打破双亲委派机制。因此,正确的去实现一个自定义类加载器的方式是重写 findClass 方法,这样不会破坏双亲委派机制

实际上,我们不应该打破双亲委派机制。因此,正确的去实现一个自定义类加载器的方式是重写 findClass 方法,这样不会破坏双亲委派机制

<aside> 💡 自定义类加载器如果不指定父类加载器,默认是 应用程序类加载器

Untitled

</aside>

<aside> 💡 两个自定义类加载器加载相同限定名的类不会冲突吗?

Untitled

</aside>

<aside> 💡 总结:自定义类加载器并重写 loadClass 方法,就可以将双亲委派机制的代码去除

</aside>

线程上下文类加载器

JDBC 中使用 DriverManager 来管理项目中引入的不同数据库的驱动

DriverManager 类位于 rt.jar 包中,由启动类加载(可能是安全性考量)

Untitled

SPI 机制

Untitled

DriverManager 的代码实现

Untitled

DriverManager 的静态代码块调用 loadInitialDrivers() 方法

    private static void loadInitialDrivers() {
        String drivers;
        try {
            drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
                public String run() {
                    return System.getProperty("jdbc.drivers");
                }
            });
        } catch (Exception ex) {
            drivers = null;
        }
        // If the driver is packaged as a Service Provider, load it.
        // Get all the drivers through the classloader
        // exposed as a java.sql.Driver.class service.
        // ServiceLoader.load() replaces the sun.misc.Providers()

        AccessController.doPrivileged(new PrivilegedAction<Void>() {
            public Void run() {

                ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
                Iterator<Driver> driversIterator = loadedDrivers.iterator();

                /* Load these drivers, so that they can be instantiated.
                 * It may be the case that the driver class may not be there
                 * i.e. there may be a packaged driver with the service class
                 * as implementation of java.sql.Driver but the actual class
                 * may be missing. In that case a java.util.ServiceConfigurationError
                 * will be thrown at runtime by the VM trying to locate
                 * and load the service.
                 *
                 * Adding a try catch block to catch those runtime errors
                 * if driver not available in classpath but it's
                 * packaged as service and that service is there in classpath.
                 */
                try{
                    while(driversIterator.hasNext()) {
                        driversIterator.next();
                    }
                } catch(Throwable t) {
                // Do nothing
                }
                return null;
            }
        });

        println("DriverManager.initialize: jdbc.drivers = " + drivers);

        if (drivers == null || drivers.equals("")) {
            return;
        }
        String[] driversList = drivers.split(":");
        println("number of Drivers:" + driversList.length);
        for (String aDriver : driversList) {
            try {
                println("DriverManager.Initialize: loading " + aDriver);
                Class.forName(aDriver, true,
                        ClassLoader.getSystemClassLoader());
            } catch (Exception ex) {
                println("DriverManager.Initialize: load failed: " + ex);
            }
        }
    }