双亲委派模型相关知识点整理

参考:

类的生命周期

Java体系中4中类加载器

双亲委派模型的约束

在所有的类加载器中除了启动类加载器(BootstrapClassLoader)之外,所有的类加载器都有自己的父类加载器。

双亲委派模型的工作机制

当一个类加载器收到类加载的请求时,不会自己主动加载这个请求而是委派到父类加载器,以此类推将该请求委派到最高级类加载器即启动类加载器。
只有当父类加载器向子类加载器反馈无法加载该请求的时候,子类才能加载该请求。

为什么需要双亲委派?(好处)

1. 【通过委派的方式,可以避免类的重复加载】当父类加载器已经加载过某一个类时,子类加载器就不会再重新加载这个类。
2. 【通过双亲委派的方式,保证安全性】因为启动类加载器在加载的时候只会加载JAVA_HOME中的jar包里面的类可以防止加载到恶意篡改的类。

“父子类加载类”之间的关系是继承吗?

在双亲委派模型中,类加载器之间的父子关系不会以继承(Inheritance)的关系来实现而是以组合(Composition)关系来复用父加载器中的代码。

为ClassLoader中的父加载器的定义:

public abstract class ClassLoader{
// The parent class loader for delegation
private final ClassLoader parent;
}

"双亲委派模型”如何实现?

1. 先检查要加载的类是否已经被加载过。
2. 若没有加载则调用父类加载器的loadClass()方法进行加载。
3. 若父类加载器为空则默认使用启动类加载器作为父类加载器。
4. 若父类加载器启动失败,抛出ClassNotFoundException异常后,再调用自己的findClass()方法进行加载。
实现双亲委派的代码位于java.lang.ClassLoader的loadClass()方法之中
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{
synchronized(getClassLoadingLock(name)){
// 1️⃣First, check if the class has alreadly been loaded
Class<?> c = findLoadedClass(name);
if(c == null){
long t0 = System.nanoTime();
try{
if(parent != null){
c = parent.loadClass(name, false);
}
c = findBootstrapClassOrNull(name);
}
}catch(ClassNotFoundException e){
// 2️⃣ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if(c == null){
// 3️⃣If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// 4️⃣this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassName().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if(resolve){
resolveClass(c);
}
return c;
}
}

如何主动破坏双亲委派机制?

在了解双亲委派模型如何实现的基础上(4 steps),想要破坏双亲委派机制就自定义一个类加载器,重写其中的loadClass方法,使其不进行双亲委派即可。

loadClass()、findClass()、defineClass()区别

如何在不破坏双亲委派模型的基础上去构造自己的类加载器

我们直到loadClass()方法中的逻辑是如果父类加载器向子类反馈无法进行加载即抛出ClassNotFoundException。
则子类加载器直接调用findClass()通过位置或文件名称直接进行类的加载。
所以我们可以通过继承ClassLoader并重写findClass()方法的内部逻辑即可完成。

破坏双亲委派模型的例子

SPI(Service Provider Interface, 是一种服务发现机制,通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类。

JDBC破坏双亲委派模型

建立数据库连接6大步骤: 1. 加载JDBC驱动程序 2. 建立连接 3. 创建命令发送器 4. 执行SQL语句 5. 处理结果集 6. 关闭数据库

  1. 加载JDBC驱动程序: Class.forName("com.mysql.jdbc.Driver");
  2. 建立连接: Connection conn = DriverManager.getConnection(url, username, password);
  3. 创建命令发送器: Statement(执行不带参数SQL)、PreparedStatement(执行带参数或不带参数的SQL)、CallableStatement(执行数据库存储过程调用);
  4. 执行SQL语句: executeQuery(sql) -> ResultSet、execute(sql) -> boolean、executeUpdate(sql) -> int;
  5. 处理ResultSet结果集: 通过executeQuery(sql) 返回结果集
  6. 关闭数据库: eg: conn.close()、stmt.close();

我们在日常开发中需要使用大量的接口包括Java内部API和第三方服务提供的SPI
Java内部API由类加载器进行加载而SPI按照双亲委派的原则无法被类加载器加载需要破坏双亲委派的机制来调用。

JDBC破坏双亲委派原则的例子如下:

在Java中为了获取指定数据库连接要用到第三方数据库服务商提供的数据库驱动

获取数据库连接的步骤
首先要加载Java提供的DriverManager类

Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mysql", "root", "1234");

然后在DriverManager类中需要调用ServiceLoader

ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);

而ServiceLoader来获取各个数据库服务提供商对Driver接口的所有实现类才能获得数据库的连接
但是Driver接口的实现类是由第三方提供属于SPI所以必须破坏双亲委派原则才能加载Driver的实现类
在JDBC的ServiceLoader类是通过构建ThreadContextLoader即线程上下文加载器来加载Driver实现类最终
由DriverManager调用从而能够获取与数据库的连接

public static<S> ServiceLoader<S> load(Class<S> service){
// 1. 获取当前线程的线程上下文类加载器
ClassLoader c1 = Thread.currentThread().getContextClassLoader();
// 2. 用于加载ClassPath中的具体实现类
return ServiceLoader.load(service, c1);
}

Tomcat破坏双亲委派模型

Tomcat是一个Web容器,一个Web容器中需要部署多个应用程序。而这些应用程序可能会依赖同一个第三方类库的不同版本

双亲委派模型的好处之前提到 1.避免重复加载相同的类 2. 安全

如果采用双亲委派模型则无法加载多个相同的类。所以,Tomcat破坏双亲委派模型,提供隔离机制,为每个Web应用单独提供一个WebAppClassLoader加载器。
WebAppClassLoader负责加载本身目录下的class文件,加载不到时才会交给CommonClassLoader加载,这个机制和双亲委派模型正好相反。

posted @   Felix_Openmind  阅读(101)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
*{cursor: url(https://files-cdn.cnblogs.com/files/morango/fish-cursor.ico),auto;}
点击右上角即可分享
微信分享提示