Java 类加载器(ClassLoader)

时间:2022-11-06 17:00:55

类加载器 ClassLoader

什么是类加载器?

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

类加载器的结构:

Java 类加载器(ClassLoader)

  • 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类加载器&lpar;ClassLoader&rpar;

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

  2. 深入理解Java类加载器&lpar;ClassLoader&rpar; (转&rpar;

    转自: 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类加载器&lpar;ClassLoader&rpar;

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

  8. Java类加载器&lpar;死磕5&rpar;

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

  9. Java类加载器&lpar; 死磕9&rpar;

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

随机推荐

  1. PYTHON 写函数,检查获取传入列表或元组对象的所有奇数位索引对应的元素,并将其作为新列表返回给调用者

    def a3(arg): ret = [ ] for i in range(len(arg)): if i % 2 == 1: ret.append(arg[i]) else: pass return ...

  2. 【Django】Django 定时任务实现(django-crontab&plus;command)

    一.编写自定义django-admin命令 注:利用django-admin自定义命令我们可以ORM框架对model进行操作,如:定时更新数据库,检测数据库状态..... Django为项目中每一个应 ...

  3. NSDate,NSNumber,NSValue

    NSDate #import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { @autoreleas ...

  4. 转:Android官方MVP架构示例项目解析

    转自: http://www.infoq.com/cn/articles/android-official-mvp-architecture-sample-project-analysis 作者 吕英 ...

  5. JavaScript解析json

    后台数据经常以json数据格式传回前台,解析当然首选JSON对象. JSON对象有两个方法,使用JSON.parse(str)可以将json字符串解析成js中的对象. var o = JSON.par ...

  6. sitecore(key&bsol;value&bsol;language)的灵活应用

    1.当我们在做网站的时候是否会因为一个页面的文字变动来回改变.这样的麻烦sitecore都帮我们解决了. 2.sitecore分类key和value和语言几个维度.不同的key会因为不同的语言显示不同 ...

  7. spring &colon;Log4j各级别日志重复打印

    使用filter进行日志过滤 这个其实是Log4j自带的方案,也是推荐方案,不知道为什么网上的资料却很少提到这点. 把log4j.properties配置文件修改成如下: #root日志 log4j. ...

  8. Sensor传感器(摇一摇)

    <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content ...

  9. 跟着未名学Office – 整体了解 Ms Office 2010

    目录 MS Office 2010    2 Microsoft Office System    2 Ribbon(功能区)    2 文件选项卡    3 SmartArt    3 屏幕截图   ...

  10. 【LOJ】&num;2056&period; 「TJOI &sol; HEOI2016」序列

    题解 这个我们处理出来每一位能变化到的最大值和最小值,包括自身 然后我们发现 \(f[i] = max(f[i],f[j] + 1) (mx[j] <= a[i] && a[j] ...