个人名片:

【实战JVM】打破双亲委派机制之线程上下文类加载器_类加载器


🐼作者简介:一名大三在校生,喜欢AI编程🎋
🐻❄️个人主页🥇:落798. 🐼
RabbitMQ快速入门🔥每日一句:🍭我很忙,但我要忙的有意义!
欢迎评论 💬点赞👍🏻 收藏 📂加关注+




文章目录

  • 线程上下文类加载器


线程上下文类加载器

利用上下文类加载器加载类,比如JDBC和JNDI等。
我们来看下JDBC的案例:
1、JDBC中使用了DriverManager来管理项目中引入的不同数据库的驱动,比如mysql驱动、oracle驱动。

package classloader.broken;//package com.itheima.jvm.chapter02.classloader.broken;

import com.mysql.cj.jdbc.Driver;

import java.sql.*;

/**
 * 打破双亲委派机制 - JDBC案例
 */

public class JDBCExample {
    // JDBC driver name and database URL
    static final String JDBC_DRIVER = "com.mysql.cj.jdbc.Driver";
    static final String DB_URL = "jdbc:mysql:///bank1";

    //  Database credentials
    static final String USER = "root";
    static final String PASS = "123456";

    public static void main(String[] args) {
        Connection conn = null;
        Statement stmt = null;
        try {
            conn = DriverManager.getConnection(DB_URL, USER, PASS);
            stmt = conn.createStatement();
            String sql;
            sql = "SELECT id, account_name FROM account_info";
            ResultSet rs = stmt.executeQuery(sql);

            //STEP 4: Extract data from result set
            while (rs.next()) {
                //Retrieve by column name
                int id = rs.getInt("id");
                String name = rs.getString("account_name");

                //Display values
                System.out.print("ID: " + id);
                System.out.print(", Name: " + name + "\n");
            }
            //STEP 5: Clean-up environment
            rs.close();
            stmt.close();
            conn.close();
        } catch (SQLException se) {
            //Handle errors for JDBC
            se.printStackTrace();
        } catch (Exception e) {
            //Handle errors for Class.forName
            e.printStackTrace();
        } finally {
            //finally block used to close resources
            try {
                if (stmt != null)
                    stmt.close();
            } catch (SQLException se2) {
            }// nothing we can do
            try {
                if (conn != null)
                    conn.close();
            } catch (SQLException se) {
                se.printStackTrace();
            }//end finally try
        }//end try
    }//end main
}//end FirstExample

2、DriverManager类位于rt.jar包中,由启动类加载器加载。

【实战JVM】打破双亲委派机制之线程上下文类加载器_类加载器_02

3、依赖中的mysql驱动对应的类,由应用程序类加载器来加载。

【实战JVM】打破双亲委派机制之线程上下文类加载器_加载_03

在类中有初始化代码:

【实战JVM】打破双亲委派机制之线程上下文类加载器_类加载器_04

DriverManager属于rt.jar是启动类加载器加载的。而用户jar包中的驱动需要由应用类加载器加载,这就违反了双亲委派机制。(这点存疑,一会儿再讨论)

那么问题来了,DriverManager怎么知道jar包中要加载的驱动在哪儿?
1、在类的初始化代码中有这么一个方法LoadInitialDrivers:

【实战JVM】打破双亲委派机制之线程上下文类加载器_类加载器_05

2、这里使用了SPI机制,去加载所有jar包中实现了Driver接口的实现类。

【实战JVM】打破双亲委派机制之线程上下文类加载器_类加载器_06

3、SPI机制就是在这个位置下存放了一个文件,文件名是接口名,文件里包含了实现类的类名。这样SPI机制就可以找到实现类了。

【实战JVM】打破双亲委派机制之线程上下文类加载器_类加载器_07

【实战JVM】打破双亲委派机制之线程上下文类加载器_类加载器_08

4、SPI中利用了线程上下文类加载器(应用程序类加载器)去加载类并创建对象。

【实战JVM】打破双亲委派机制之线程上下文类加载器_类加载器_09

总结:

【实战JVM】打破双亲委派机制之线程上下文类加载器_类加载器_10

JDBC案例中真的打破了双亲委派机制吗?

最早这个论点提出是在周志明《深入理解Java虚拟机》中,他认为打破了双亲委派机制,这种由启动类加载器加载的类,委派应用程序类加载器去加载类的方式,所以打破了双亲委派机制。

但是如果我们分别从DriverManager以及驱动类的加载流程上分析,JDBC只是在DriverManager加载完之后,通过初始化阶段触发了驱动类的加载,类的加载依然遵循双亲委派机制。

所以我认为这里没有打破双亲委派机制,只是用一种巧妙的方法让启动类加载器加载的类,去引发的其他类的加载。


欢迎评论 💬点赞👍🏻 收藏 📂加关注+