类加载机制(七):线程上下文类加载器


title: 类加载机制(七):线程上下文类加载器
date: 2019-03-21 20:14:06
categories:

  • Java虚拟机
    tags:
  • 类加载机制
  • 线程上下文类加载器

双亲委托机制的破坏

我们知道,class文件的加载是按照双亲委托机制完成的,这个机制解决了各个类加载器的基础类的统一问题,因为上层类加载器加载的类对下层加载的类是可见的,所以这些基础类可以被Java程序所调用,但是如果这些基础类需要调用用户所写的类呢,可下层类加载器加载的类不是对上层类加载器加载的类是透明的吗?这种情况不是不可能的,比如JDBC,它位于rt.jar包下,是由启动类加载器加载,而它却需要classPath下的接口提供者(Service Provider Interface)的代码,但是启动类不可能去加载这些代码,那怎么办呢?

SPI(Service Provider Interface)
只提供了接口的声明,具体实现由厂商完成。某些SPI需要调用由厂商实现并部署在classPath下的接口实现代码。这些接口由启动类加载器去加载,但启动类加载器不认识classPath下的代码。

这时候就需要对双亲委托机制进行“破坏”了,Java设计者设计了一种叫做“线程上下文类加载器”的机制,当这些接口需要实现的代码时,就去使用这个线程上下文类加载器完成这些接口实现代码的加载。

线程上下文类加载器

线程上下文类加载器contextClassLoader,其实,我们在分析Launcher类的源码时,已经遇到过了:

Thread.currentThread().setContextClassLoader(this.loader);

Launcher类的构造方法中,执行了这条语句,并将系统类加载器传进去,我们跟着去看看Thread的源码。

  private ClassLoader contextClassLoader;

内部维护了一个类加载器,也就是线程上下文类加载器。

 if (security == null || isCCLOverridden(parent.getClass()))
            this.contextClassLoader = parent.getContextClassLoader();
        else
            this.contextClassLoader = parent.contextClassLoader;

Threadinit方法中,线程继承其父线程的上下文类加载器。

public void setContextClassLoader(ClassLoader cl) {
   
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
   
            sm.checkPermission(new RuntimePermission("setContextClassLoader"));
        }
        contextClassLoader = cl;
    }

Thread中,可以设置线程类加载器,也就是说,若如果没有通过setContextClassLoader进行设置的话,线程将继承其父线程的上下文类加载器。前面说过,在Launcher类中设置了线程上下文类加载为系统类加载器,即在Java程序中未设置线程上下文类加载器的话,线程上下文类加载器就为系统类加载器。

深入理解线程上下文类加载器

线程上下文类加载器的一般使用模式
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
try{
   
	Thread.currentThread().setContextClassLoader(targetccl);
	doMethod();
}finally{
   
	Thread.currentThread().setContextClassLoader(classLoader);
}

线程上下文类加载器的一般使用模式(获取-使用-还原)

doMethod()里面调用了Thread.currentThread().setContextClassLoader()来获取当前线程的上下文类加载器做某些事情。

ServiceLoader类

在双亲委托模型下,类加载是由下至上的,即下层的类加载器会委托上层进行加载。但是对于SPI来说。有些接口是Java核心库所提供的,而Java核心库是由启动类加载器来加载的,而这些接口的实现却是来自于不同的jar包(由各独立厂商实现),Java的启动类加载器是不会加载其他来源的jar包,这样就导致一些接口由启动类加载加载,实现由其他加载器加载,传统的双亲委托机制就会无法满足SPI的要求。而通过给当前线程设置上下文类加载器,就可以由设置的上下文类加载器来实现对于接口实现类的加载 。就比如JDBC驱动,如下图所示。

首先,在pom文件中添加了关于MySql的依赖,我们通过代码找到项目中的驱动。

public class MyTest17 {
   
    public static void main(String[] args){
   
        //通过ServiceLoader去加载驱动
        ServiceLoader<Driver> drivers = ServiceLoader.load(Driver.class
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值