类加载器 ClassLoader

什么是类加载器?

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

类加载器的结构:

  • 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();
}
}
} }

Java 类加载器(ClassLoader)的更多相关文章

  1. 深入理解Java类加载器(ClassLoader)

    深入理解Java类加载器(ClassLoader) Java学习记录--委派模型与类加载器 关于Java类加载双亲委派机制的思考(附一道面试题) 真正理解线程上下文类加载器(多案例分析) [jvm解析 ...

  2. 深入理解Java类加载器(ClassLoader) (转)

    转自: http://blog.csdn.net/javazejian/article/details/73413292 关联文章: 深入理解Java类型信息(Class对象)与反射机制 深入理解Ja ...

  3. 浅析java类加载器ClassLoader

    作为一枚java猿,了解类加载器是有必要的,无论是针对面试还是自我学习. 本文从JDK提供的ClassLoader.委托模型以及如何编写自定义的ClassLoader三方面对ClassLoader做一 ...

  4. 潜水 java类加载器ClassLoader

    类加载器(class loader)用于装载 Java 类到 Java 虚拟机中.一般来说.Java 虚拟机使用 Java 类的方式例如以下:Java 源程序(.java 文件)在经过 Java 编译 ...

  5. java类加载器——ClassLoader

    Java的设计初衷是主要面向嵌入式领域,对于自定义的一些类,考虑使用依需求加载原则,即在程序使用到时才加载类,节省内存消耗,这时即可通过类加载器来动态加载. 如果你平时只是做web开发,那应该很少会跟 ...

  6. Java类加载器ClassLoader总结

    JAVA类装载方式,有两种: 1.隐式装载, 程序在运行过程中当碰到通过new 等方式生成对象时,隐式调用类装载器加载对应的类到jvm中. 2.显式装载, 通过class.forname()等方法,显 ...

  7. Java类加载器(ClassLoader)

    类加载的机制的层次结构 每个编写的”.java”拓展名类文件都存储着需要执行的程序逻辑,这些”.java”文件经过Java编译器编译成拓展名为”.class”的文件,”.class”文件中保存着Jav ...

  8. Java类加载器(死磕5)

    Java类加载器(  CLassLoader )  死磕5:  自定义一个文件系统classLoader 本小节目录 5.1. 自定义类加载器的基本流程 5.2. 入门案例:自定义文件系统类加载器 5 ...

  9. Java类加载器( 死磕9)

    [正文]Java类加载器(  CLassLoader ) 死磕9:  上下文加载器原理和案例 本小节目录 9.1. 父加载器不能访问子加载器的类 9.2. 一个宠物工厂接口 9.3. 一个宠物工厂管理 ...

随机推荐

  1. Oracle个人自学笔记

    SET LINESIZE 300;//设置每一行的长度 SET PAGESIZE 100;//设置每一列的长度 CONN 用户名/密码 [AS SYSDBA],如果是sys用户一定要加上SYSDBA ...

  2. Elasticsearch 如何使用RESTful API

    所有其他语言可以使用 RESTful API 通过端口 9200 和 Elasticsearch 进行通信,你可以用你最喜爱的 web 客户端访问 Elasticsearch .事实上,正如你所看到的 ...

  3. Mate Linux 桌面的什么受GNOME 2 粉丝喜欢 ?

    导读 如果你以前听过这个传闻:当 GNOME3 第一次发布时,很多 GNOME 用户还没有准备好放弃 GNOME 2. Mate(以马黛茶yerba mate植物命名)项目的开始是为了延续 GNOME ...

  4. 任意promise串行执行算法 - 童彪

      // 任意promise串行执行算法 - 童彪 function runAllPromise() { var p1 = new Promise((resove, reject) => { s ...

  5. 把链接生成二维码 二维码中间带有logo

    在工程中引入三个文件jquery.qrcode.js.qrcode.js.utf.js.其中utf.js文件是防止链接中的参数出现中文乱码现象 jquery.qrcode.js文件 function ...

  6. redhat 7.6 rsync 配置,实时同步脚本

    1.查看rsync,并安装 yum install rsync -y 2.配置/etc/rsyncd.conf文件 建议cp一份作为备份,清空内容复制以下配置 [服务端配置]log file = /v ...

  7. Spring Boot整合Mybatis(注解方式和XML方式)

    其实对我个人而言还是不够熟悉JPA.hibernate,所以觉得这两种框架使用起来好麻烦啊. 一直用的Mybatis作为持久层框架, JPA(Hibernate)主张所有的SQL都用Java代码生成, ...

  8. 查漏补缺之go依赖管理

    vendor 使用vendor进行包管理,首先要保证项目在$GOPATH/src/路径下(踩过坑),然后build时就会按照如图所示的优先级进行包的搜索. 一个没有找到包的实例: module 其他 ...

  9. Spring 通读官方文档

    Spring 通读官方文档 这部分参考文档涵盖了Spring Framework绝对不可或缺的所有技术. 其中最重要的是Spring Framework的控制反转(IoC)容器.Spring框架的Io ...

  10. JDBC--调用函数&存错过程

    1.通过Connection对象的prepareCall()方法创建CallableStatement对象的实例,在使用prepareCall()方法时需传入一个String类型的字符串,该字符串用于 ...