java查看上下文加载器_【深入理解Java虚拟机 】线程的上下文类加载器

线程上下文类加载器

线程上下文类加载器( Thread Context ClassLoader) 是从JDK1.2 引入的,类Thread 的getContextClassLoader() 与 setContextClassLoader(Classloader var1) 分别用来设置线程的上下文类加载器。如果没有指定线程的上下文的加载器,那么线程将会继承父线程的上下文类加载器。Java 的初始化线程的上下文加载器,可以通过上下文类加载器加载类与资源。

基本的获取和使用方法:

public class ContextClassLoader {

/**

* 每个类都会使用自己的类加载器尝试去加载所依赖的类

*

*

如果ClassX 依赖了 ClassY ,那么在ClassX的加载器将会在主动引用ClassY 并且ClassY尚未被加载的时候加载ClassY 这个类

*/

public static void main(String[] args) {

System.out.println(Thread.currentThread().getContextClassLoader());

System.out.println(Thread.class.getClassLoader());

}

}

从 JDBC 说起

在以前学习JDBC 的时候我们最深刻的印象应该是 Class.forName("xxxxxx"); 简单的伪代码如下图:

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.ResultSet;

import java.sql.Statement;

public class DbUtil {

public static final String URL = "jdbc:mysql://localhost:3306/db";

public static final String USER = "root";

public static final String PASSWORD = "123456";

public static void main(String[] args) throws Exception {

Class.forName("com.mysql.jdbc.Driver");

Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);

Statement stmt = conn.createStatement();

ResultSet rs = stmt.executeQuery("SELECT user_name, age FROM student");

}

}

}

从上面的代码中,import导入的 jdbc 的相关类可以看到,他们均来自于 java.sql 包下,根据类的双亲委派机制可以非常清晰的知道,这些接口肯定是来自于 系统类加载器加载。那么具体的其实现类使用我们的应用类加载器加载,所以问题出现 其中的JDBC 标准是由启动器类加载器加载,而具体的实现的类是由系统类加载器加载,所以这就会导致启动类加载器加载的JDBC标准类无法找到子加载器加载的JDBC实现

TCC 的作用: 改变双亲委托模型

上面的实现模式,我们称之为SPI (服务提供接口) ,这种服务方式通过类的双亲委派机制就会出现问题。

这是加载器双亲委派模型的一个缺陷,但是JVM设计者做了一个不太优雅的的方式解决,就是线程上下文类的加载器,  父 ClassLoader 可以使用 Thread.currentThread().getContextClassloader() 所制定的Classloader 加载的类,这就改变了父ClassLoader不能使用子ClassLoader加载的类以及其他没有父子关系的加载器加载类的访问情况,即改变了双亲委托模型。

Java中所有涉及SPI的加载动作都采用这种方式, 实现方案包括:  JDBC、JNDI、JCE以及JBI等

TCC 的使用模式

线程上下文的使用模式主要分为: 获取-> 设置 -> 使用 -> 还原 , 伪代码可以参考:

// 获取原类加载器

ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

// 通过一些手段获取的目标类加载器

ClassLoader targetClassLoader = xxxx;

try {

// 将新的类加载器设置为当前上下文类加载器

Thread.currentThread().setContextClassLoader(targetClassLoader);

// 使用加载器加载一些自己需要的类

loadClass();

} finally {

// 还原

Thread.currentThread().setContextClassLoader(classLoader);

}

总结

当高层提供了统计的接口让低层去实现,同时又要在高层加载(或者实例化)这个类,那么就必须通过线程上下文类加载器帮助高层ClassLoader 加载这个类

父加载器不能访问使用子加载器加载的类,子加载器可以访问使用父加载器加载的类

就SPI服务而言,有些接口是启动类加载器加载,但具体的实现各个厂商有自己不同的实现方式,这些实现是不会被启动类加载器加载的,这样传统的双亲委托机制就无法满足 SPI 的要求。而通过设置当前线程的上下类加载器,就可以通过当前线程的上下文类加载器加载这些类。

Java中所有涉及SPI的加载动作都采用这种方式, 实现方案包括:  JDBC、JNDI、JCE以及JBI等

拓展学习点

Tomcat 中的类加载机制

SpringWeb 中类实现的机制

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值