关于双亲委派

88 阅读3分钟

介绍

JVM加载类是一个很严格的过程。相关可以参考类加载

Spring是单例的,在加载过程中为保证类是唯一的,通过委派父级加载器加载类的方式来保证,不同的加载器加载同一个class文件也是不同的类。另一方面,让核心类由内置类加载器来加载可以一定程度上保护程序安全。

类的加载器选择为bootstrapClassLoader -> ExtClassLoader-> AppClassLoader -> CustomerClassLoader

分析

 When requested to find a class or resource, a <tt>ClassLoader</tt> instance will delegate the 
 search for the class or resource to its parent class loader before attempting to find the class 
 or resource itself.  The virtual machine's built-in class loader, called the "bootstrap class loader",
 does not itself have a parent but may serve as the parent of a <tt>ClassLoader</tt> instance.

以上节选自ClassLoader.java。大致意思是当加载一个类或者资源的时候,ClassLoader实例会试图使用其父级加载器来加载目标对象资源。JVM的内置类加载器称之为bootstrap class loader,它没有父级加载器但是可以作为其他类加载器的父级。

public Launcher() {
    Launcher.ExtClassLoader var1;
    try {
        //创建ExtClassLoader 主要用来加载\ext下的扩展包
        var1 = Launcher.ExtClassLoader.getExtClassLoader();
    } catch (IOException var10) {
        throw new InternalError("Could not create extension class loader", var10);
    }

    try {
        //调用classLoader的方法设置父级加载器为var1[ExtClassLoader]
        //主要用来加载第三方jar包资源
        this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
    } catch (IOException var9) {
        throw new InternalError("Could not create application class loader", var9);
    }

    //设置容器类加载器 与ServiceLoader有关
    Thread.currentThread().setContextClassLoader(this.loader);
    String var2 = System.getProperty("java.security.manager");
    if (var2 != null) {
        SecurityManager var3 = null;
        if (!"".equals(var2) && !"default".equals(var2)) {
            try {
                var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();
            } catch (IllegalAccessException var5) {
            } catch (InstantiationException var6) {
            } catch (ClassNotFoundException var7) {
            } catch (ClassCastException var8) {
            }
        } else {
            var3 = new SecurityManager();
        }

        if (var3 == null) {
            throw new InternalError("Could not create SecurityManager: " + var2);
        }

        System.setSecurityManager(var3);
    }

}

以上节选自Launcher.java

加载类,首先委托父级加载器加载,最后选择BootstrapClassLoader。

protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        // First, check if the class has already been loaded
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }

            if (c == null) {
                // If still not found, then invoke findClass in order
                // to find the class.
                long t1 = System.nanoTime();
                c = findClass(name);

                // this is the defining class loader; record the stats
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}

以上节选自ClassLoader.java

SPI 打破双亲委派模式,使用ServiceLoader加载方式选择合适的实现方式。

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);
            /** ServiceLoader.java
            public static <S> ServiceLoader<S> load(Class<S> service) {
                ClassLoader cl = Thread.currentThread().getContextClassLoader();
                return ServiceLoader.load(service, cl);
            }
            */
            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{
                //重写了迭代器的方法 使用LazyIterator来加载服务
                /*
                * ServiceLoader(private class LazyIterator)
                */
                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);
        }
    }
}

以上节选自DriverManager.java

LazyIterator加载服务

private boolean hasNextService() {
    if (nextName != null) {
        return true;
    }
    if (configs == null) {
        try {
            String fullName = PREFIX + service.getName();
            if (loader == null)
                configs = ClassLoader.getSystemResources(fullName);
            else
                configs = loader.getResources(fullName);
        } catch (IOException x) {
            fail(service, "Error locating configuration files", x);
        }
    }
    while ((pending == null) || !pending.hasNext()) {
        if (!configs.hasMoreElements()) {
            return false;
        }
        pending = parse(service, configs.nextElement());
    }
    nextName = pending.next();
    return true;
}

以上节选自ServiceLoader.java


参考

  1. 类加载
  2. SPI