双亲委派模型相关知识点整理
参考:
类的生命周期

Java体系中4中类加载器

双亲委派模型的约束

双亲委派模型的工作机制
只有当父类加载器向子类加载器反馈无法加载该请求的时候,子类才能加载该请求。
为什么需要双亲委派?(好处)
2. 【通过双亲委派的方式,保证安全性】因为启动类加载器在加载的时候只会加载JAVA_HOME中的jar包里面的类可以防止加载到恶意篡改的类。
“父子类加载类”之间的关系是继承吗?
为ClassLoader中的父加载器的定义:
public abstract class ClassLoader{
// The parent class loader for delegation
private final ClassLoader parent;
}
"双亲委派模型”如何实现?
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;
}
}
如何主动破坏双亲委派机制?
loadClass()、findClass()、defineClass()区别

如何在不破坏双亲委派模型的基础上去构造自己的类加载器
则子类加载器直接调用findClass()通过位置或文件名称直接进行类的加载。
所以我们可以通过继承ClassLoader并重写findClass()方法的内部逻辑即可完成。
破坏双亲委派模型的例子
SPI(Service Provider Interface, 是一种服务发现机制,通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类。
JDBC破坏双亲委派模型
建立数据库连接6大步骤: 1. 加载JDBC驱动程序 2. 建立连接 3. 创建命令发送器 4. 执行SQL语句 5. 处理结果集 6. 关闭数据库
- 加载JDBC驱动程序: Class.forName("com.mysql.jdbc.Driver");
- 建立连接: Connection conn = DriverManager.getConnection(url, username, password);
- 创建命令发送器: Statement(执行不带参数SQL)、PreparedStatement(执行带参数或不带参数的SQL)、CallableStatement(执行数据库存储过程调用);
- 执行SQL语句: executeQuery(sql) -> ResultSet、execute(sql) -> boolean、executeUpdate(sql) -> int;
- 处理ResultSet结果集: 通过executeQuery(sql) 返回结果集
- 关闭数据库: 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加载,这个机制和双亲委派模型正好相反。

浙公网安备 33010602011771号