十七、类加载器总结及扩展类加载器要点分析

本文详细探讨了Java类加载器的双亲委托模型,解释了其如何确保Java核心库的类型安全,防止核心类库被自定义类替代,并介绍了不同类加载器如何创建额外的命名空间。同时,文章还讲解了扩展类加载器和根类加载器的工作原理,以及如何通过设置系统类加载器来影响类的加载过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、类加载器双亲委托模型的好处
  • 1、可以确保java核心库的类型安全:所有的java应用都至少会引用java.lang.Object类,也就是说在运行期,这个类会被加载到虚拟机中;如果这个加载过程是由自定义类加载器所完成的,那么很可能就会在jvm中存在多个版本的Object类,而且这些类之间还是不兼容的,相互不可见的(是因为命名空间)。借助于双亲委托机制,java核心类库中的加载动作都是由启动类加载器来统一完成加载工作,从而保证了java应用所使用的都是同一个版本的java核心类,他们之间是相互兼容的。
  • 2、可以确保java核心类库所提供的类不会被自定义的类所替代。
  • 3、不同的类加载器可以为相同名称的类创建额外的命名空间。相同名称的类可以并存在java虚拟机中,只需要用不同的类加载器来加载他们即可。不同类加载器所加载的类是不见兼容的。这就相当于在java虚拟机内部创建了一个又一个相互隔离的java类空间,这类技术在很多框架中都得到了实际应用。
2、扩展类加载器的加载

扩展类加载器只能加载jar包中的class文件,不能直接去加载class文件。也就是说class文件必须打成jar包
测试,建包:C:\Windows\Sun\Java\lib\ext,这是十六当中的一个扩展类加载器加载的包,将com\jvm\classloader包以及里面的User.class文件打成jar包,放入新建的包中。

public class MyTest22 {
    static {
        System.out.println("MyTest22 initializer");
    }

    public static void main(String[] args) {
        System.out.println(MyTest22.class.getClassLoader());
        System.out.println(User.class.getClassLoader());
    }
}

运行结果:

MyTest22 initializer
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$AppClassLoader@18b4aac2

如果不以jar包的形式放进去的话,只是单纯的把com\jvm\classloader包结构以及User.class文件放进去,则结果为:

MyTest22 initializer
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$AppClassLoader@18b4aac2
3、根类加载器的加载

如果把根类加载器的加载路径换了,则直接报错,因为肯定找不到Object类的class文件。

4、类加载器是由谁来加载的呢

当jvm启动时,一块特殊的机器码会运行,他会加载扩展类加载器和系统类加载器,这块特殊的机器码叫做启动类加载器(BootStrap)。
启动类加载器不是java类,其他加载器都是java类。启动类加载器是特定于平台的及其指令,他负责开启整个的加载过程。
所有的类加载器(除了启动类加载器)都被实现为java类。不过,总归有一个组件来加载第一个java类加载器,从而让整个加载过程能够顺利进进行下去,加载第一个纯java类加载器就是启动类加载器的职责。
启动类加载器还会负责加载供JRE正常运行所需要你的基本组件,这包括java.util与java.lang包中的类等等。

public class MyTest22 {
    public static void main(String[] args) {
        System.out.println(ClassLoader.class.getClassLoader());
        System.out.println(Launcher.class.getClassLoader());
    }
}

appclassloader和extclassloader都是定义在Launcher中的内部类,所以可以观察Launcher的加载器来确定那两个类的加载器。
可以看到,确实是由根加载器加载的。

5、可以设置系统类加载器

ClassLoader.getSystemClassLoader()的文档:

Returns the system class loader for delegation. This is the default delegation parent for new ClassLoader instances, and is typically the class loader used to start the application.
This method is first invoked early in the runtime’s startup sequence, at which point it creates the system class loader and sets it as the context class loader of the invoking Thread.
The default system class loader is an implementation-dependent instance of this class.
If the system property “java.system.class.loader” is defined when this method is first invoked then the value of that property is taken to be the name of a class that will be returned as the system class loader. The class is loaded using the default system class loader and must define a public constructor that takes a single parameter of type ClassLoader which is used as the delegation parent. An instance is then created using this constructor with the default system class loader as the parameter. The resulting class loader is defined to be the system class loader.
If a security manager is present, and the invoker’s class loader is not null and the invoker’s class loader is not the same as or an ancestor of the system class loader, then this method invokes the security manager’s checkPermission method with a RuntimePermission(“getClassLoader”) permission to verify access to the system class loader. If not, a SecurityException will be thrown.

### Java 扩展类加载器加载 JAR 包的原理及实现 Java 的扩展类加载器(Extension Class Loader)负责加载位于标准扩展目录中的 JAR 文件。这些文件通常存储在 `$JAVA_HOME/lib/ext` 或由 `java.ext.dirs` 系统属性指定的位置中。 #### 1. 类加载机制概述 Java 虚拟机提供三种主要的类加载器:引导类加载器(Bootstrap ClassLoader)、扩展类加载器(Extension ClassLoader),以及应用程序类加载器(Application ClassLoader)。这三者遵循 **双亲委派模型** 进行工作[^2]。这意味着当一个类加载器收到加载请求时,它会先委托给其父级加载器尝试加载该类;只有在其父级无法完成加载的情况下,才会自行处理。 #### 2. 扩展类加载器的作用范围 扩展类加载器主要用于加载那些不属于核心 JDK API 的库文件,但仍然希望被所有应用程序共享使用的组件。这类库一般存放在上述提到的标准扩展目录路径下。一旦有新的 JAR 文件放入此位置,在 JVM 启动过程中便会自动识别并加载它们而无需额外配置。 #### 3. 加载过程详解 以下是关于扩展类加载器具体是如何查找和加载JAR包的过程描述: - 当需要加载某个特定名称空间下的Class对象实例化之前, - 首先按照双亲委派原则逐层向上查询直至到达最顶层即bootstrap class loader为止; - 如果仍未找到目标,则轮到当前层次——也就是extension class loader执行实际操作。 对于每一个待解析的名字串形式表示的目标类别标识符string representation of the fully qualified name, extension class loader 将依次扫描预定义好的ext dir(s),寻找匹配项所在的archive file(.jar/.zip etc.) 并从中提取相应字节码数据流用于后续编译链接阶段的工作流程之中[^3]。 #### 4. 实现细节分析 从技术角度来看,整个搜索逻辑可以概括如下几个关键步骤: - 构建URLClassLoader子类LaunchedURLClassLoader作为最终执行实体; - 设置基础资源定位地址列表(通常是基于环境变量或者命令行参数传递过来的具体物理磁盘上的绝对路径); - 对于每一次新增加进来的新条目(entry),都会调用addURL方法将其加入内部维护的一个Set集合当中去避免重复添加相同的内容源; - 在真正开始检索前还需要初始化一些必要的辅助结构比如cache table等等以便提高效率减少不必要的IO开销. 这里给出一段简化版伪代码展示基本思路: ```java public class ExtensionClassLoader extends URLClassLoader { public ExtensionClassLoader(URL[] urls){ super(urls); // Initialize with URLs pointing to ext dirs. } @Override protected synchronized Class<?> findClass(String name) throws ClassNotFoundException{ String path = name.replace('.', '/') + ".class"; for (URL url : this.getURLs()){ try(InputStream is = url.openStream(); InputStream resourceAsStream = getResourceAsStream(path)){ byte[] b = new byte[resourceAsStream.available()]; resourceAsStream.read(b); return defineClass(name,b,0,b.length); }catch(IOException e){continue;} } throw new ClassNotFoundException(name); } } ``` 以上就是有关Java扩展类加载器加载JAR包的基本原理及其背后涉及的一些重要概念和技术要点.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值