zoukankan      html  css  js  c++  java
  • Java 类加载器(ClassLoader)

    类加载器 ClassLoader

    什么是类加载器?

    • 通过一个类的全限定名来获取描述此类的二进制字节流这个动作放到Java虚拟机外部去实现, 以便让应用程序自己决定如何去获取所需要的类。实现这个动作的代码模块被称为 "类加载器"。

    类加载器的结构:

    1570698408264

    • BootstrapClassLoader

      • 启动类加载器, 用来加载<JAVA_HOME>/jre/lib 路径, -Xbootclasspath参数指定的路径以<JAVA_HOME>/jre/classes中的类。
      • 是由C++实现的
    • ExtClassLoader(Ext --> Extension)

      • 拓展类类加载器, 它用来加载<JAVA_HOME>/jre/lib/ext路径以及java.ext.dirs系统变量指定的类路径下的类。
    • AppClassLoader

      • 应用程序类类加载器, 主要加载应用程序ClassPath下的类 (包含jar包中的类)。
      • 是java应用默认的类加载器
    • 用户自定义加载器

      • 用户根据自定义需求, 自由的定制加载的逻辑, 继承AppClassLoader, 仅仅覆盖findClass(), 即将继续遵守双亲委派模型
    • ThreadContextClassLoader

      • 线程上下文加载器, 它不是一个新的类型, 更像一个类加载器的角色, ThreadContextClassLoader可以是上述类加载器的任意一种, 但是往往是AppClassLoader

    在虚拟机启动时会初始化BootstrapClassLoader, 然后在Launcher类中去加载ExtClassLoader, AppClassLoader,并将AppClassLoader的parent设置为ExtClassLoader, 并设置线程上下文类加载器。

    Launcher: JRE中用于启动程序入口main()的类(sun.misc包下)

    public class Launcher {
        // URL 流处理工厂
        private static URLStreamHandlerFactory factory = new Launcher.Factory();
        // 初始化发行器
        private static Launcher launcher = new Launcher();
        
        // 启动的类的路径
        private static String bootClassPath = System.getProperty("sun.boot.class.path");
        // 私有的类加载器
        private ClassLoader loader;
        // URL流处理器
        private static URLStreamHandler fileHandler;
    
        public static Launcher getLauncher() {
            return launcher;
        }
    
        public Launcher() {
            Launcher.ExtClassLoader var1;
            try {
                // 加载拓展类类加载器(先加载AppClassLoader的父类加载器)
                var1 = Launcher.ExtClassLoader.getExtClassLoader();
            } catch (IOException var10) {
                throw new InternalError("Could not create extension class loader", var10);
            }
    
            try {
                // 加载应用程序类类加载器, 并将其父加载器var1(ExtClassLoader) 传入
                // 再加载AppClassLoader加载器
                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安全管理器信息
            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);
            }
    
        }
    

    双亲委派模型:

    • 当一个类加载器去加载类时先尝试让父类加载器去加载, 如果父类加载加载不了再尝试自身加载。
    • 双亲委派模型能保证基础类仅加载一次, 不会让jvm中存在重名的类。
    • java核心类都是BootstrapClassLoader加载的, 保证了java的安全与稳定性

    ClassLoader的loadClass方法:

    // 传入类名称 和 是否解析标签
    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) {
                            // 递归使用父类的loadClass方法
                            c = parent.loadClass(name, false);
                        } else {
                            // 如果没有父类, 就通过Bootstrap类加载器加载该类
                            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.
                        // 如果没有找到, 就请求findClass来找到该类
                        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;
            }
        }
    

    子类只需要实现findClass, 关心从哪里加载即可。parent需要自己设置, 可以放在构造函数设置。

    注意点: AppClassLoader和ExtClassLoader都是Launcher的静态类, 都是包访问路径权限的。

    如何自定义ClassLoader?

    让我们来看一个apache-flink-core plugin包中的PluginLoader类的实现:

    package org.apache.flink.core.plugin;
    
    import org.apache.flink.annotation.VisibleForTesting;
    import org.apache.flink.util.ArrayUtils;
    import org.apache.flink.util.ChildFirstClassLoader;
    
    import javax.annotation.concurrent.ThreadSafe;
    
    import java.util.Iterator;
    import java.util.ServiceLoader;
    
    /**
     * A {@link PluginLoader} is used by the {@link PluginManager} to load a single plugin. It is essentially a combination of a {@link ChildFirstClassLoader} and {@link ServiceLoader}. 
     * 该类被用于加载一个单独的插件, 它本质上是一个由子类的第一个加载器和服务加载器组成的组合件
     * This class can locate and load service implementations from the plugin for a given SPI. The {@link PluginDescriptor}, which among other information contains the resource
     * URLs, is provided at construction 
     * 该类可以从已得的SPI定位并且加载服务应用,插件描述器包含了在结构中提供的资源的URL
     */
    @ThreadSafe
    public class PluginLoader {
    
    	/** Classloader which is used to load the plugin classes. We expect this classloader is thread-safe.*/
        // 被用于加载插件类的看类加载器, 我们期望该类加载器是线程安全的。
    	private final ClassLoader pluginClassLoader;
    
    	@VisibleForTesting
    	public PluginLoader(ClassLoader pluginClassLoader) {
    		this.pluginClassLoader = pluginClassLoader;
    	}
    
        // 通过插件描述器 和 父类加载器 创建插件类加载器
    	@VisibleForTesting
    	public static ClassLoader createPluginClassLoader(PluginDescriptor pluginDescriptor, ClassLoader parentClassLoader, String[] alwaysParentFirstPatterns) {
    		return new ChildFirstClassLoader(
    			pluginDescriptor.getPluginResourceURLs(),
    			parentClassLoader,
                 // 合并总是父类优先模型的String数组 与 通过插件描述器 获取的 排除模型加载器 String数组
    			ArrayUtils.concat(alwaysParentFirstPatterns, pluginDescriptor.getLoaderExcludePatterns()));
    	}
    	// 通过插件描述器 和 父类加载器 创建插件类加载器
    	public static PluginLoader create(PluginDescriptor pluginDescriptor, ClassLoader parentClassLoader, String[] alwaysParentFirstPatterns) {
    		return new PluginLoader(createPluginClassLoader(pluginDescriptor, parentClassLoader, alwaysParentFirstPatterns));
    	}
    
    	/**
    	 * Returns in iterator over all available implementations of the given service interface (SPI) for the plugin.
    	 * 在迭代器返回所有可获取的已得的实现了服务接口的插件
    	 *
    	 * @param service the service interface (SPI) for which implementations are requested.
    	 * @param <P> Type of the requested plugin service.
    	 * @return An iterator of all implementations of the given service interface that could be loaded from the plugin.
         * 返回一个所有已得的服务接口的可以从插件中被加载的实现类
    	 */
    	public <P extends Plugin> Iterator<P> load(Class<P> service) {
    		try (TemporaryClassLoaderContext classLoaderContext = new TemporaryClassLoaderContext(pluginClassLoader)) {
    			return new ContextClassLoaderSettingIterator<>(
    				ServiceLoader.load(service, pluginClassLoader).iterator(),
    				pluginClassLoader);
    		}
    	}
    
    	/**
    	 * Wrapper for the service iterator. The wrapper will set/unset the context classloader to the plugin classloader around the point where elements are returned.
    	 * 服务迭代器的包装类, 在元素被返回时, 该包装器会设置或取消设置 上下文类加载器 到插件类加载器
    	 * @param <P> type of the iterated plugin element.
    	 * 迭代的插件元素类型
    	 */
    	static class ContextClassLoaderSettingIterator<P extends Plugin> implements Iterator<P> {
    
    		private final Iterator<P> delegate;
    		private final ClassLoader pluginClassLoader;
    
    		ContextClassLoaderSettingIterator(Iterator<P> delegate, ClassLoader pluginClassLoader) {
    			this.delegate = delegate;
    			this.pluginClassLoader = pluginClassLoader;
    		}
    
    		@Override
    		public boolean hasNext() {
    			return delegate.hasNext();
    		}
    
    		@Override
    		public P next() {
    			try (TemporaryClassLoaderContext classLoaderContext = new TemporaryClassLoaderContext(pluginClassLoader)) {
    				return delegate.next();
    			}
    		}
    	}
    
    }
    
  • 相关阅读:
    14.RabbitMQ
    13.跨域
    12.EF
    11.Redis
    GitHub获取用户ID
    10.AOP
    第26节课:pytest结合Allure操作
    第25节课:pytest测试框架
    第二十四节课:requests爬虫实战
    第二十三节课:正则表达式re模块:
  • 原文地址:https://www.cnblogs.com/ronnieyuan/p/11885463.html
Copyright © 2011-2022 走看看