做问卷的网站好,asp.net网站开发实例教程 下载,行业网站建设详解,什么叫百度竞价推广前言#xff1a;
在Java编程中#xff0c;类加载器(Class Loader)扮演着重要的角色。类加载器负责加载Java字节码并将其转换为可执行对象#xff0c;使得我们能够在应用程序中使用各种类和资源。Java类加载器的设计和实现旨在支持动态扩展和模块化编程#xff0c;为Java语…前言
在Java编程中类加载器(Class Loader)扮演着重要的角色。类加载器负责加载Java字节码并将其转换为可执行对象使得我们能够在应用程序中使用各种类和资源。Java类加载器的设计和实现旨在支持动态扩展和模块化编程为Java语言提供了很大的灵活性和可维护性。
类加载器的分类是理解Java类加载机制的关键部分。不同类型的类加载器负责加载不同类型的类它们有着不同的加载策略和优先级。在本文中我们将深入探讨类加载器的分类并详细介绍每一种类加载器的特点和使用场景。
目录
前言
类加载器
什么是类加载器
类加载器的应用场景
类加载器的分类JDK8以及之前版本
双亲委派机制
什么是双亲委派机制
双亲委派机制源码解读
双亲委派机制的作用
打破双亲委派机制
总结 类加载器
什么是类加载器
类加载器ClassLoader是JAVA虚拟机提供给应用程序去实现类和接口字节码数据的技术。 类加载器只参与加载过程中字节码获取并加载到内存中这一部分 类加载器的应用场景 动态加载类加载器允许在运行时动态地加载新的类和资源。这对于需要根据特定条件或用户需求加载不同类的应用程序非常有用。例如插件系统和模块化开发中经常会使用类加载器动态加载和卸载模块实现灵活的扩展和功能定制。 热部署类加载器可以实现热部署即在应用程序运行过程中替换和更新已加载的类。这使得我们可以在不停止应用程序的情况下对代码进行更新和修复。热部署在开发和调试阶段非常有用能够提高开发效率和调试体验。 版本隔离通过使用不同的类加载器加载不同版本的类我们可以实现类的版本隔离。这在应用程序升级和依赖管理中十分重要避免了不同版本的类之间的冲突和兼容性问题。 安全控制类加载器可以应用安全策略限制或控制特定代码和资源的访问权限。通过自定义类加载器我们可以实现自定义的安全策略对加载的类进行权限控制和验证。 字节码增强和动态代理类加载器可以用于实现字节码增强和动态代理。通过自定义类加载器我们可以在加载类的过程中修改字节码添加额外的逻辑或功能。这对于AOP面向切面编程和代理模式等技术有着重要的应用。
而且类加载器的双亲委派机制也是面试中必问的一块内容只要面试官问JVM相关的内容那么双亲委派机制就一定是必问项。因此我们要学好类的加载器相关内容。
在了解了什么是类加载器以及其应用场景之后我们来正式学习类加载器。 类加载器的分类JDK8以及之前版本 启动类加载器Bootstrap ClassLoader它是JVM的一部分负责加载Java核心类库如java.lang包中的类。它是最顶层的类加载器通常使用C实现无法在Java代码中直接获取到。(通用且重要) 扩展类加载器Extension ClassLoader它负责加载Java的扩展库通常位于JRE的lib/ext目录下。它是由Java编写的是由启动类加载器加载的。通用但是不重要 应用程序类加载器Application ClassLoader也称为系统类加载器System ClassLoader它负责加载用户类路径Classpath上指定的类库。它是开发者最常使用的类加载器也是默认的类加载器。 除了上述三种常见的类加载器之外还可以通过继承 java.lang.ClassLoader 类来自定义类加载器。自定义类加载器可以灵活加载类实现各种特定需求比如从网络下载类文件、解密等。 总结起来常见的类加载器可以分为启动类加载器、扩展类加载器、应用程序类加载器。开发者也可以根据需要自定义类加载器。 而在实际JAVA代码中我们可能会遇到一个JAR包同时存在于多个类加载器加载范围的情况此时我们就需要双亲委派机制来解决这个问题
双亲委派机制
什么是双亲委派机制
双亲委派机制Parent Delegation Model是Java类加载器的一种工作方式用于保证类的加载安全性和一致性。
根据双亲委派机制当一个类加载器需要加载某个类时它首先会委派给它的父类加载器去尝试加载。如果父类加载器能够成功加载该类那么就返回该类的Class对象。如果父类加载器无法加载该类子类加载器才会尝试自己去加载。 简单来讲双亲委派机制的核心是解决一个类到底由谁进行加载的问题。而其过程就是向上查找是否加载过和向下尝试加载 注意点不能认为一个加载器与其父类加载器的关系是继承虽然有“父”。而本质是在加载器内部创建一个ClassLoader来存储其父类加载器。 需要注意的是如果我们尝试获取扩展类加载器的parent对象得到的结果是null。并不是说其父类不是启动类加载器只是因为启动类加载器属于JVM的一部分使用C实现无法被获取到 双亲委派机制源码解读
整个双亲委派机制都是在Classload中进行的因此我们主要看这部分源码 尝试加载一个类的时候我们会调用loadClass方法该方法的第一个参数为加载的类名第二个参数为是否对类进行解析
进入loadClass方法 其实这段代码还是比较容易看懂的整体的逻辑为
使用findLoadedClass寻找目标类是否被加载如果目标类没有被加载cnull那么就尝试寻找当前加载器的父类加载器如果有父类加载器parentnull就把当前类交给父类加载器执行loadClass方法。如果没有父类加载器就让启动类加载器BootstrapClassLoad进行查找并加载如果一直到顶层加载器仍然无法加载目标类那么我们就交由当前加载器进行加载cfindClassname并且记录一下时间等各种信息然后return 0如果目标类已经被加载直接return 0
最后我们看一下 cfindClassname的findClass源码 双亲委派机制的作用 避免重复加载通过使用双亲委派机制每个类加载器在尝试加载某个类之前都会先委托给它的父类加载器。这样可以避免同一个类被多个不同的类加载器加载保证类的一致性避免重复加载带来的冲突和内存浪费。 安全性保证核心类库如Java的核心类库由启动类加载器负责加载用户自定义的类则由应用程序类加载器加载。这样可以确保核心类库的安全性防止用户自定义的类篡改核心类库的行为。 类的隔离性不同的类加载器加载的类位于不同的命名空间中彼此之间互相隔离。即使两个类的全限定名相同但由不同的类加载器加载的类在JVM中也被视为不同的类。这种隔离性可以有效避免类的冲突使得每个类加载器都可以独立加载和管理类。 扩展性通过自定义类加载器可以扩展Java的类加载机制实现特定的加载需求。开发者可以自定义类加载器来实现类似热部署、动态加载等功能。自定义类加载器可以继承父类加载器的特性并根据业务需求进行扩展。
总的来说双亲委派机制可以保证类的一致性、安全性和隔离性避免重复加载同时也提供了灵活的扩展性使得类加载器可以根据特定需求进行定制。
而虽然双亲委派机制为JAVA类的加载提供了很好的安全性和便捷性。但是有的时候我们不得不打破双亲委派机制例如
一个Tomcat容器中可以运行多个WEB应用而如果这两个应用中出现了同名的A类那么Tomcat就要保证这两个A类都被加载并且是各自不同的类。如果不打破双亲委派机制那么WEB1中的A类记载后WEB2中自己的A类就不会加载成功了按照双亲委派机制来讲此时会直接返回WEB1中的A类。此时我们就需要打破双亲委派机制。
打破双亲委派机制
1.自定义类加载器并且重写ClassloadTomcat使用策略
通过上文我们对源码单独阅读相信大家已经理解了双亲委派机制的基本流程。而我们如果想要打破双亲委派机制重写一下Classload方法就好具体地讲是重写以下代码块 代码示例
public class MyClassLoader extends ClassLoader {Overrideprotected Class? loadClass(String name, boolean resolve) throws ClassNotFoundException {synchronized (getClassLoadingLock(name)) {// 首先检查类是否已经被加载Class? c findLoadedClass(name);if (c null) {// 检查类是否在系统类加载器中已经加载c findClass(name);}if (resolve) {resolveClass(c);}return c;}}Overrideprotected Class? findClass(String name) throws ClassNotFoundException {// 在这里实现自定义的类加载逻辑// 可以从其他位置加载类的字节码并使用 defineClass() 方法定义类}
}
但需要注意的是在这段代码的逻辑中虽然我们没有给自定义类加载任何父类加载器但是他也会有一个默认的父类加载器 应用程序类加载器只不过我们重写loadClass的时候并没有用到父类加载器而已。 如果我们只是想自定义一个加载器自主加载一些类。此时就不应该打破双亲委派机制而是选择在FindClass中进行重写 2.线程上下文类加载器JDBC使用策略
JDBC在尝试连接数据库的时候会使用到一个叫做DriveManager的包来管理各种数据库驱动和加载相关驱动
String url jdbc:mysql://localhost:3306/your_database_name;
String username your_username;
String password your_password;
Connection connection DriverManager.getConnection(url, username, password); DriveManager位于rt.jar中由启动类加载器进行加载。
而这个包又要去加载各种数据库驱动类。而这种第三方的包又要在应用程序加载类中进行加载。那么就出现了一个问题 也就是说启动类加载器加载完DriveManager之后对于其需要加载的各种数据库驱动启动类加载器是无法进行加载的他只能交给应用程序类加载器进行加载。这不就打破了双亲委派机制的从下向上委托原则。
我们一步一步来解析首先先来介绍一下SPI机制 SPIService Provider Interface是Java提供的一种服务提供发现机制。它允许开发人员定义服务接口并允许第三方厂商通过在应用程序的类路径下提供实现来扩展应用程序的功能。
SPI机制的工作原理如下首先开发人员定义一个服务接口以及对该接口提供服务实现的一个或多个类。然后在应用程序的类路径中创建一个配置文件该文件的名称必须是META-INF/services/接口全限定名其中以接口全限定名作为文件名其内容则是服务接口实现类的全限定名。
当应用程序初始化时Java运行时会利用Java的反射机制从类路径下的配置文件中读取并加载服务接口的实现类。这样应用程序就能够获取到实现类的实例并使用其提供的功能。
而我们的DriveManager就是通过SPI机制快速找到JaR包要加载的驱动的。
而基于SPI机制DriveManager被加载的整体过程为 在了解了基于基于SPI机制下DriveManager被加载的整体过程后我们再来想一想SPI机制是如何打破双亲委托机制下的从下委托的呢
在SPI机制中通常使用线程上下文类加载器Thread Context Class Loader来加载具体的实现类。线程上下文类加载器是在多线程环境中引入的概念用于指定每个线程的类加载器。线程上下文类加载器通常通过Thread.currentThread().setContextClassLoader()方法进行设置。
在SPI机制中通过线程上下文类加载器可以解决在双亲委托模型下从底层向上委托的问题。具体来说当SPI实现框架的代码位于一个类库中而由应用程序自定义的SPI实现类位于应用程序的类路径下时由于双亲委托模型的限制无法直接由应用程序加载SPI实现类。此时可以通过在应用程序中使用线程上下文类加载器来加载SPI实现类即将线程上下文类加载器设置为应用程序的类加载器。这样SPI实现框架就可以通过线程上下文类加载器加载应用程序中的SPI实现类从而打破了双亲委托模型的限制。
需要注意的是SPI机制依赖于线程上下文类加载器的正确设置因此在使用SPI机制时需要确保正确设置线程上下文类加载器以保证SPI实现框架能够正确加载应用程序中的SPI实现类。 简单来讲SPI有上下文类加载器他可以提前保存好一个应用类程序加载器。然后当我们使用启动类加载器加载DriveManager而DriveManager需要加载数据库驱动的时候DriveManager就会调用上下文类加载器使得当前加载器从启动类加载变为应用类加载器 但其实对于上下文加载器打破双亲委派机制这种方式呢普遍还是存在争议的。
有人认为他确实打破双亲委派机制因为 DriveManager 由启动类加载器加载却在记载过程中需要委派程序类加载器进行记载打破了双亲委派机制的委派是从上到下的规则。有人认为他没有打破双亲委派机制因为在整个加载类的过程中DriveManager在java核心包rt.jar中因此被启动类加载器加载jar包中的数据库驱动属于第三方包因此被从应用程序类加载器加载。不管是DriveManager类还是数据库驱动类的加载都没有重写loadClass方法只要你使用的是原生的loadClass你就仍然遵循双亲委派机制
3.OSGI框架的类加载器
历史上OSGI模块化框架打破了双亲委派机制它存在同级之间的类记载器的委托加载。 OSGi开放服务网关是一个用于构建模块化、动态、可扩展的Java应用程序的规范和框架。 模块化是指将应用程序拆分为多个独立的模块也称为bundle每个模块包含自己的代码和资源。这种模块化的设计使得开发人员可以更加灵活地管理和维护应用程序提高了可重用性和可维护性。 最早的时候JAVA是没有模块化的思想的所有的jar包都在rt.jar中进行管理而OSGi就提供了一种方式将功能相近的jar包放入到一个jar包进行统一管理。 在OSGi框架中每个模块被称为一个bundle捆绑包bundle可以包含自己的类和资源。OSGi使用了自己的类加载器实现称为BundleClassLoader。
BundleClassLoader是OSGi框架中的核心类加载器它在加载类时打破了双亲委派机制。它首先尝试自己加载类如果找不到所需的类则会委托给父类加载器。这种机制与标准的双亲委派机制不同因为BundleClassLoader首先尝试自己加载并不一定按照父优先的原则。
总结
类加载器和双亲委派机制是Java中关键的概念。通过类加载器Java程序能够动态加载类实现代码的灵活性和可扩展性。双亲委派机制保证了类的唯一性和一致性避免了重复加载和冲突。了解类加载器和双亲委派机制对于解决类路径冲突、实现模块化开发和保证安全性至关重要。它们对于掌握Java动态加载能力和模块化开发具有重要意义。深入理解其原理和机制能更好地利用Java的灵活性和可扩展性开发出优秀的应用程序。在某些情况下可能需要定制和扩展类加载器行为进一步发挥其作用。总之类加载器和双亲委派机制是Java虚拟机的核心组成部分对于理解和掌握Java的动态加载能力和模块化开发至关重要。
如果我的内容对你有帮助请点赞评论收藏。创作不易大家的支持就是我坚持下去的动力