上下文类加载器

【JVM】线程上下文类加载器详解 SPI机制简介 SPI的全名为Service Provider Interface,主要是应用于厂商自定义组件或插件中。在java.util.ServiceLoader的文档里有比较详细的介绍。简单的总结下java SPI机制的思想:我们系统里抽象的各个模块,往往有很多不同的实现方案,比如日志模块、xml解析模块、jdbc模块等方案。面向的对象的设计里,我们一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。一旦代码里涉及具体的实现类,就违反了可拔插的原则,如果需要替换一种实现,就需要修改代码。为了 阅读详情
  1. Bootstrap ClassLoader/启动类加载器主要负责jdk_home/lib目录下的核心 api 或 -Xbootclasspath 选项指定的jar包装入工作。
  2. Extension ClassLoader/扩展类加载器
    主要负责jdk_home/lib/ext目录下的jar包或 -Djava.ext.dirs 指定目录下的jar包装入工作。
  3. System ClassLoader/系统类加载器
    主要负责java -classpath/-Djava.class.path所指的目录下的类与jar包装入工作。
  4. User Custom ClassLoader/用户自定义类加载器(java.lang.ClassLoader的子类)
    在程序运行期间, 通过java.lang.ClassLoader的子类动态加载class文件, 体现java动态实时类装入特性。

类加载器的特性:

  1. 每个ClassLoader都维护了一份自己的名称空间, 同一个名称空间里不能出现两个同名的类。
  2. 为了实现java安全沙箱模型顶层的类加载器安全机制, java默认采用了 " 双亲委派的加载链 " 结构。

类图中, BootstrapClassLoader是一个单独的java类, 其实在这里, 不应该叫他是一个java类。因为,它已经完全不用java实现了。它是在jvm启动时, 就被构造起来的, 负责java平台核心库。

自定义类加载器加载一个类的步骤

ClassLoader 类加载逻辑分析, 以下逻辑是除 BootstrapClassLoader 外的类加载器加载流程:

  1. // 检查类是否已被装载过  
  2. Class c = findLoadedClass(name);   
  3. if (c == null ) {   
  4.       // 指定类未被装载过  
  5.       try {   
  6.           if (parent != null ) {   
  7.               // 如果父类加载器不为空, 则委派给父类加载  
  8.              c = parent.loadClass(name,  false );   
  9.          }  else {   
  10.               // 如果父类加载器为空, 则委派给启动类加载加载  
  11.              c = findBootstrapClass0(name);   
  12.          }   
  13.      }  catch (ClassNotFoundException e) {   
  14.           // 启动类加载器或父类加载器抛出异常后, 当前类加载器将其  
  15.           // 捕获, 并通过findClass方法, 由自身加载  
  16.          c = findClass(name);   
  17.      }   
  18. }  

线程上下文类加载器
java默认的线程上下文类加载器是 系统类加载器(AppClassLoader)。

  1. // Now create the class loader to use to launch the application  
  2. try  {   
  3.     loader = AppClassLoader.getAppClassLoader(extcl);   
  4. }  catch (IOException e) {   
  5.      throw new InternalError(   
  6. "Could not create application class loader" );   
  7. }    
  8.   
  9. // Also set the context class loader for the primordial thread.  
  10. Thread.currentThread().setContextClassLoader(loader);  
Java代码 复制代码  收藏代码
  1. // Now create the class loader to use to launch the application  
  2. try {   
  3.     loader = AppClassLoader.getAppClassLoader(extcl);   
  4. } catch (IOException e) {   
  5.     throw new InternalError(   
  6. "Could not create application class loader" );   
  7. }    
  8.   
  9. // Also set the context class loader for the primordial thread.  
  10. Thread.currentThread().setContextClassLoader(loader);  
// Now create the class loader to use to launch the application
try {
    loader = AppClassLoader.getAppClassLoader(extcl);
} catch (IOException e) {
    throw new InternalError(
"Could not create application class loader" );
} 

// Also set the context class loader for the primordial thread.
Thread.currentThread().setContextClassLoader(loader);

以上代码摘自sun.misc.Launch的无参构造函数Launch()。

使用线程上下文类加载器, 可以在执行线程中, 抛弃双亲委派加载链模式, 使用线程上下文里的类加载器加载类.
典型的例子有, 通过线程上下文来加载第三方库jndi实现, 而不依赖于双亲委派.
大部分java app服务器(jboss, tomcat..)也是采用contextClassLoader来处理web服务。
还有一些采用 hotswap 特性的框架, 也使用了线程上下文类加载器, 比如 seasar (full stack framework in japenese).

线程上下文从根本解决了一般应用不能违背双亲委派模式的问题.
使java类加载体系显得更灵活.

随着多核时代的来临, 相信多线程开发将会越来越多地进入程序员的实际编码过程中. 因此,
在编写基础设施时, 通过使用线程上下文来加载类, 应该是一个很好的选择。

当然, 好东西都有利弊. 使用线程上下文加载类, 也要注意, 保证多根需要通信的线程间的类加载器应该是同一个,
防止因为不同的类加载器, 导致类型转换异常(ClassCastException)。

 

为什么要使用这种双亲委托模式呢?

  1. 因为这样可以避免重复加载,当父亲已经加载了该类的时候,就没有必要子ClassLoader再加载一次。
  2. 考虑到安全因素,我们试想一下,如果不使用这种委托模式,那我们就可以随时使用自定义的String来动态替代java核心api中定义类型,这样会存在非常大的安全隐患,而双亲委托的方式,就可以避免这种情况,因为String已经在启动时被加载,所以用户自定义类是无法加载一个自定义的ClassLoader。

java动态载入class的两种方式:

  1. implicit隐式,即利用实例化才载入的特性来动态载入class
  2. explicit显式方式,又分两种方式:
    1. java.lang.Class的forName()方法
    2. java.lang.ClassLoader的loadClass()方法

用Class.forName加载类

Class.forName使用的是被调用者的类加载器来加载类的。
这种特性, 证明了java类加载器中的名称空间是唯一的, 不会相互干扰。
即在一般情况下, 保证同一个类中所关联的其他类都是由当前类的类加载器所加载的。

  1. public static Class forName(String className)   
  2.       throws ClassNotFoundException {   
  3.       return forName0(className, true , ClassLoader.getCallerClassLoader());   
  4. }    
  5.   
  6. /** Called after security checks have been made. */  
  7. private static native Class forName0(String name, boolean  initialize,   
  8. ClassLoader loader)   
  9.       throws ClassNotFoundException;  
Java代码 复制代码  收藏代码
  1. public static Class forName(String className)   
  2.      throws ClassNotFoundException {   
  3.      return forName0(className, true , ClassLoader.getCallerClassLoader());   
  4. }    
  5.   
  6. /** Called after security checks have been made. */  
  7. private static native Class forName0(String name, boolean initialize,   
  8. ClassLoader loader)   
  9.      throws ClassNotFoundException;  
public static Class forName(String className)
     throws ClassNotFoundException {
     return forName0(className, true , ClassLoader.getCallerClassLoader());
} 

/** Called after security checks have been made. */
private static native Class forName0(String name, boolean initialize,
ClassLoader loader)
     throws ClassNotFoundException;

上面中 ClassLoader.getCallerClassLoader 就是得到调用当前forName方法的类的类加载器

static块在什么时候执行?

  • 当调用forName(String)载入class时执行,如果调用ClassLoader.loadClass并不会执行.forName(String,false,ClassLoader)时也不会执行.
  • 如果载入Class时没有执行static块则在第一次实例化时执行.比如new ,Class.newInstance()操作
  • static块仅执行一次

各个java类由哪些classLoader加载?

  • java类可以通过实例.getClass.getClassLoader()得知
  • 接口由AppClassLoader(System ClassLoader,可以由ClassLoader.getSystemClassLoader()获得实例)载入
  • ClassLoader类由bootstrap loader载入

NoClassDefFoundError和ClassNotFoundException

  • NoClassDefFoundError:当java源文件已编译成.class文件,但是ClassLoader在运行期间在其搜寻路径load某个类时,没有找到.class文件则报这个错
  • ClassNotFoundException:试图通过一个String变量来创建一个Class类时不成功则抛出这个异常

为什么要使用Thread context classloader

 

类A调用类B,B会要求调用它的类的类加载器来加载它,也就是B会要求加载A的加载器来加载B。这就会有个问题,如果他们在一起,那没关系,肯定某个classloader会把它们俩都加载好。但是如果A在/lib/ext文件夹中,而B在Classpath中呢?过程是这样的首先加载A,那么一层层上到Bootstrap Classloader,boot没找到所以ext自己找,找到了,没问题;加载B,因为A调用了B,所以也从bootstrap来找,没找到,然后A的ext classloader来找还是没找到,但是再也不会往下调用了,于是报出ClassNotFoundException。

但是现实生活中有很多应用,比如JDBC核心方法在核心库而驱动在扩展库,是必定在两个地方的,那怎么办呢?要用到Context ClassLoader我们在建立一个线程Thread的时候,可以为这个线程通过setContextClassLoader方法来指定一个合适的classloader作为这个线程的context classloader,当此线程运行的时候,我们可以通过getContextClassLoader方法来获得此context classloader,就可以用它来载入我们所需要的Class。

默认的是system classloader。利用这个特性,我们可以“打破”classloader委托机制了,父classloader可以获得当前线程的context classloader,而这个context classloader可以是它的子classloader或者其他的classloader,那么父classloader就可以从其获得所需的 Class,这就打破了只能向父classloader请求的限制了。这个机制可以满足当我们的classpath是在运行时才确定,并由定制的 classloader加载的时候,由system classloader(即在jvm classpath中)加载的class可以通过context classloader获得定制的classloader并加载入特定的class(通常是抽象类和接口,定制的classloader中是其实现),例如web应用中的servlet就是用这种机制加载的.

Thread.currentThread().getContextClassLoader()的意义:

父Classloader可以使用当前线程Thread.currentthread().getContextLoader()中指定的classloader中加载的类。

颠覆了父ClassLoader不能使用子Classloader或者是其它没有直接父子关系的Classloader中加载的类这种情况。

这个就是Context Class Loader的意义。



 实战演练


(1)步骤一

上图进行了这样一个实验:首先一个名为Class(1)的类中启动MainThread(其实就是这个类里面有main函数的意思啦),注意这个类的名字后面标出了其所在的路径(即ClassPath),然后在里面进行测试,发现目前它的装载器和当前线程(MainThread)的ContextClassLoader都是AppClassLoader。然后Class(1)启动了一个新线程Class(2)。这里的Class(2)是一个Thread的子类,执行Class(2)代码的线程我称之为Thread-0。

(2)步骤二

上图可以看到Class(2)的装载器和ContextClassLoader同样都是AppClassLoader。随后我在Class(2)中创建了一个新的URLCLassLoader,并用这个ClassLoader来载入另一个和Class(1)不在同一个ClassPath下的类Class(3)。此时我们就可以看到变化:即载入Class(3)的装载器是URLClassLoader,而ContextClassLoader还仍然是AppClassLoader。

(2)步骤三

最后我们在Class(3)中启动了一个线程类Class(4),发现Class(4)也是由URLClassLoader载入的,而此时ContextClassLoader仍然是AppClassLoader。

    在整个过程中,装载类的ClassLoader发生了变化,由于线程类Class(4)是由Class(3)启动的,所以装载它的类装载器就变成了URLClassLoader。与此同时,所有线程的ContextClassLoader都继承了生成该线程的ContextClassLoader--AppClassLoader。

 

如果我们在第二步的结尾执行了绿色框中的代码:setContextClassLoader(),则结果就会变成下面这个样子:

我们可以清楚地看到,由于Thread-0将其ContextClassLoader设置成了URLClassLoader,而Thread-1是在Thread-0里面生成的,所以就继承了其ContextClassLoader,变成了URLClassLoader。



有三个工程

ModelA

         

public class AActivator implements IActivator{

    @Override
    public void init() {
        System.out.println("load A plugin~~~~~~~~~");
        PluginCenter center =PluginCenter.getInstance();
        center.putNotice("A", new ANotice());
        
        Thread.currentThread().setContextClassLoader(PluginCenter.getInstance().getClassLoader("B"));
        MyThread m =new MyThread();
        Thread a =new Thread(m);
        a.start();
    }

}


public class ANotice implements INotice {

    @Override
    public void noticeName() {
        System.out.println("A notice");

    }

}



public class MyThread implements Runnable {

    @Override
    public void run() {
     //Aurl,父classloader是app,MyThread是由AActivator产生的,AActivator由Aurl载入的,所以MyThread也是Aurl载入
     System.out.println("~~~@@MyThread类~~~"+MyThread.class.getClassLoader());
     //Burl,由于AActivator类里设定了上下文,如果不设定还是APP(一直继承初始的线程类装载器)
     System.out.println("~~~@@当前线程~~~"+Thread.currentThread().getContextClassLoader());
     
     try {
         Class bog =  Class.forName("com.b.Bgo", true, Thread.currentThread().getContextClassLoader());
        // Class bog  = Thread.currentThread().getContextClassLoader().loadClass("com.b.Bgo");
        // Class bog  = PluginCenter.getInstance().getClassLoader("B").loadClass("com.b.Bgo");
         CenterGo b=(CenterGo) bog.newInstance();
         System.out.println("~~**~~"+b.getGo());
       
         
         
       //报错,Bgo由当前classloader Aurl加载,它的父classloader是APP,都找不到ModleB的类  
        // Bgo pp = new Bgo();
        // Bgo  bb=(Bgo) bog.newInstance();  
    } catch (Exception e) {
         e.printStackTrace();
    }

    }


}


modelB

public class BActivator implements IActivator{

    @Override
    public void init() {
        Bgo bgo =new Bgo();
        System.out.println("load B pugin~~~~~~~~~"+bgo.getGo());
        PluginCenter center =PluginCenter.getInstance();
        center.putNotice("B", new BNotice());
    }

}




public class Bgo implements CenterGo{

    private int b = 1;

    public int getGo() {
        return b;
    }

  
    
}


public class BNotice implements INotice {

    @Override
    public void noticeName() {
        System.out.println("B notice");

    }

}

ModleCenter

public interface CenterGo {
    public int getGo();
}




public interface IActivator {
	
	/**
	 * 第一次启动插件时,调用此方法 IPluginActivator的初始化最好在这个方法中实现
	 * 因为使用无参构造方法实例化IPluginActivator时plugin的信息并不一定完整
	 */
	public void init();

}


public class Inint {

    /**
     * @param args
     */
    public static void main(String[] args) throws Exception{
 
        
        String Apath = "D:/work_AUM/ModelA/bin/";
        String Bpath = "D:/work_AUM/ModelB/bin/";
        URL aurl =  new URL("file", "", -1, Apath);
        URL burl =  new URL("file", "", -1, Bpath);
        URLClassLoader Aloader =new URLClassLoader(new URL[]{aurl});
        URLClassLoader Bloader =new URLClassLoader(new URL[]{burl});
        
        PluginCenter center =PluginCenter.getInstance();
        center.putClassLoader("A", Aloader);
        center.putClassLoader("B", Bloader);
        
        IActivator i1 = (IActivator)Aloader.loadClass("com.a.AActivator").newInstance();
        IActivator i2 = (IActivator)Bloader.loadClass("com.b.BActivator").newInstance();;
        
         //当前线程是appClassLoader
        System.out.println("~~~~~A~~~~~"+i1.getClass().getClassLoader());//Aurl
        System.out.println("~~~~~B~~~~~"+i2.getClass().getClassLoader());//Burl
        
        i1.init();
        i2.init();
        
       
        center.dispathNotice("A");
        
      
       
    }

}



public interface INotice {
   public void noticeName();
}


public class PluginCenter {

    private static PluginCenter p = new PluginCenter();
    private PluginCenter(){
        
    }
    public static PluginCenter getInstance(){
        return p;
    }
    
    private Map<String,ClassLoader> map  =new HashMap<String,ClassLoader>();
    private Map<String,INotice> map2  =new HashMap<String,INotice>();
    
    public ClassLoader getClassLoader(String name){
        return map.get(name);
    }
    
    public void putClassLoader(String name,ClassLoader l){
        this.map.put(name, l);
    }
    
    public void putNotice(String name,INotice l){
        this.map2.put(name, l);
    }
    
    public void dispathNotice(String name){
        INotice i  = map2.get(name);
        i.noticeName();
    }
    public int getNo(){
        return 1;
    }
}




[Github]GitHub 2FA快速安全配置全攻略 GitHub强制要求用户启用双重身份验证(2FA)以提升代码安全。本文详细介绍了2FA的三种实现方式:TOTP应用验证(推荐)、SMS短信验证(备用)和硬件安全密钥(最安全),并提供了配置指南。特别推荐使用在线工具2FA.run快速测试TOTP功能。文章还涵盖恢复代码管理、企业级2FA策略制定以及常见问题解决方案,如时间同步问题、多设备同步等。通过强制2FA、定期审计和建立安全事件响应流程,开发者可构建更安全的代码管理体系。 阅读详情

相关推荐

最小路径和——动态规划求解(Java实现)

最小路径和——动态规划求解(Java实现) 题目: 给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。 说明:每次只能向下或者向右移动一步。 示例: 输入: [ [1,3,1], [1,5,1], [4,2,1] ] 输出: 7 解释: 因为路径 1→3→1→1→1 的总和最小。 这题和机器人走路的思路类似,就是状态方程有所不同,...

weixin_43395911的博客 894

JVM 线程上下文类加载器

线程上下问类加载器出现的原因 Q: 越基础的类由越上层的加载器进行加载,如果基础类又要调用回用户的代码,那该怎么办? A: 解决方案:使用“线程上下文类加载器” 为了解决这个问题,Java设计团队只好引入了一个不太优雅的设计:线程上下文类加载器(Thread Context ClassLoader)。这个类加载器可以通过java.lang.Thread类的setContextClassLoaser()方法进行设置,如果创建线程时还未设置,它将会从父线程中继承一个,如果在应用程序的全局范围内都没有设置过的

Java的一只小实习生的博客 1655

【从零搭建到实战运行】基于PX4-1.13与XTDrone的FAST-LIVO2 Gazebo仿真全流程解析

本文详细解析了从零开始搭建基于PX4-1.13飞控、XTDrone仿真框架与Gazebo的FAST-LIVO2无人机仿真平台的全流程。内容涵盖环境配置、Livox雷达插件定制、算法部署与参数调试,旨在帮助开发者快速构建完整的激光-惯性-视觉SLAM仿真环境,并解决集成过程中的常见问题。

weixin_29218509的博客 520

JVM -- 类加载器;双亲委派机制;线程上下文类加载器(八)

类加载器就是根据类的二进制名(binary name)读取java编译器编译好的字节码文件(.class文件),并且转化生成一个java.lang.Class类的一个实例。每个实例用来表示一个Java类,jvm就是用这些实例来生成java对象的。如new一个String对象;反射生成一个String对象,都会用到String.class 这个java.lang.Class类的对象。基本上所有的类加载器都是java.lang.ClassLoader 类的一个实例。

MinggeQingchun的博客 1790

无人驾驶汽车系统入门(三十二)——FEC算法:点云快速欧几里得聚类实战解析

本文深入解析了FEC算法在无人驾驶点云聚类中的高效应用,通过逐点处理策略和体素栅格预分割技术,实现了比传统欧几里得聚类快百倍的速度。文章详细介绍了算法原理、Python实现及参数调优指南,并对比了FEC与传统算法的性能差异,为无人驾驶系统开发提供了实用解决方案。

weixin_29065015的博客 485

LIN总线从入门到精通:低成本汽车通信协议原理与实战

在汽车电子和嵌入式系统中,串行通信协议是实现分布式控制的关键技术。其中,LIN(Local Interconnect Network)总线作为一种专为低成本应用设计的单主多从网络协议,其核心原理在于通过简化的“领导-汇报”通信模式,在满足车身控制、传感器数据采集等场景需求的同时,将硬件与布线成本降至最低。LIN总线采用标准化的帧结构,包含间隔场、同步场和受保护标识符场,并依赖调度表来管理通信时序,确保了网络的确定性与可靠性。其技术价值在于为车窗、座椅、灯光等非安全关键系统提供了高性价比的解决方案,是构建分层

weixin_29185167的博客 260

深入浅出JVM(八)之类加载器

什么是类加载器?类加载器通过类的全限定类名进行类加载机制从而生成Class对象Class对象中包含该类相关类信息,通过Class对象能够使用反射在运行时阶段动态做一些事情显示加载与隐式加载类加载器有两种方式进行加载,一种是在代码层面显示的调用,另一种是当程序遇到创建对象等命令时自行判断该类是否进行过加载,未加载就先进行类加载显示加载:显示调用ClassLoader加载class对象隐式加载:不显示调用ClassLoader加载class对象(因为虚拟机会在第一次使用到某个类时自动加载这个类)

关注菜菜的后端私房菜,获取更多技术干货~ 1153

华为OD机试真题 新系统-字符串格式调整(C/C++/Py/Java/Js/Go)

华为OD机试 新系统真题【字符串格式调整】,提供C语言、Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解,以及在线OJ模拟做题。华为OD机试真题【字符串格式调整】多语言题解

qq_45776114的博客 1141

JVM工作原理与实战(十三):打破双亲委派机制-线程上下文类加载器

JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了打破双亲委派机制的方法、线程上下文类加载器等内容。

Rye的博客 2074

【PINN物理信息网络】基于PINN物理信息网络求解圆柱坐标中的热方程(python)

【PINN物理信息网络】基于PINN物理信息网络求解圆柱坐标中的热方程(python)

k8291121的博客 2273

PCL 点到面的ICP算法【2024最新版】

经典的点到面的ICP算法。博客长期更新,本文最新更新时间为:2024年10月18日。

点云侠的博客 36万+

【基础篇】七、线程上下文类加载器打破双亲委派机制

SPI,Service Provider Interface,是JDK内置的一种服务提供发现机制。大致流程:写个小Demo直观感受下SPI,定义对外接口: 其他类写实现类(这里接口和实现类放同一个jar中了,一般来说写接口和写实现的是两种角色): 写文件: 测试模块中引入上面的这个jar: 尝试加载: 成功获取到接口的两个实现类:JDBC使用DriverManager类来完成不同数据库厂商的驱动注册: DriverManager位于JDK的rt.jar包,由启动类加载器去加载,而引入的第三方jar包归应用

llg___的博客 1497

光通量与辐射通量之间的换算

由理论和实验可知存在两种定义: 1W1W1W(瓦特)的频率为540×1012Hz540\times10^{12}Hz540×1012Hz(赫兹)的单色辐射的辐射通量等于683lm(流明)683lm(流明)683lm(流明).。 1lm1lm1lm的频率为540×1012Hz540\times10^{12}Hz540×1012Hz的单色通量等于1683=0.001464W\frac{1}{683}=0.001464W6831​=0.001464W。 从上述定义中我们可以得到什么呢?我们知道光速: c=3×

u011699626的博客 3273

线程上下文类加载器

线程上下文类加载器 名词解释 SPI:Service Provider Interface 服务提供者接口 当前类加载器(Current ClassLoader) 每个类都会使用自己的类加载器(加载自身的类加载器)来去加载所依赖的类,如果ClassA引用了ClassB,NameClassA的类加载器就会加载ClassB(前提是ClassB没有被加载) 线程上下文类加载器(Context Clas...

闪电侠-公子的博客 1792

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

双亲委派模型与线程上下文类加载器

SPI机制是什么,有哪些应用场景,又带来了哪些问题? 双亲委派模型是Java推荐的类加载模型,但违背该模型的案例有哪些?为什么会违背,又是怎么解决这种case的? JDBC驱动加载的案例有哪些,SPI机制为它带来了哪些方便? 线程上下文类加载器的作用与应用场景?

Rico's Blogs 1万+

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

Java 类加载器(二)——线程上下文类加载器

简述 线程上下文类加载器(context class loader)是从JDK 1.2开始引入的。类 java.lang.Thread中的方法getContextClassLoader()和setContextClassLoader(ClassLoader cl)用来获取和设置线程的上下文类加载器。如果没有通过 setContextClassLoader(ClassLoader cl)方法进行设置...

Lili429的博客 920

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

jvm——线程上下文类加载器

/** * @author dc * @date 2020/6/17 - 13:05 */ /** * 当前类加载器(Current ClassLoader): * 每个类都会使用自己的类加载器(即加载自身的类加载器)去加载其他类(指的所依赖的类), * 如果classX引用classY,那么classX类加载器就会加载classY(前提是classY尚未被加载) * * 线程上下文类加载器(Context ClassLoader): * 线程上下文类加载器从JDK1.2开始引入的,类T

weixin_43925059的博客 153

真正理解线程上下文类加载器(多案例分析)

线程上下文类加载器破坏了“双亲委派模型”,可以在执行线程中抛弃双亲委派加载链模式,使上层代码可以逆向使用下层的系统类加载器。本文通过JDBC和Tomcat两个案例分析,详细解释了其中的原理。

Stay Foolish 6万+

Java线程上下文类加载器与SPI

线程上下文类加载器(context class loader)是从JDK 1.2开始引入的。类 java.lang.Thread中的方法getContextClassLoader()和setContextClassLoader(ClassLoader cl)用来获取和设置线程的上下文类加载器。如果没有通过 setContextClassLoader(ClassLoader cl)方法进行设置的话,

长青树呀 4422

类加载器-线程上下文

线程上下文类加载器 我们在使用 JDBC 时,都需要加载 Driver 驱动,不知道你注意到没有,不写 Class.forName("com.mysql.jdbc.Driver") 也是可以让 com.mysql.jdbc.Driver 正确加载的,你知道是怎么做的吗? 让我们追踪一下源码: public class DriverManager { // 注册驱动的集合 private final static CopyOnWriteArrayList<DriverInfo>

Leon_Jinhai_Sun的博客 200

类加载器-线程上下文类加载器

破坏委派模型 问题:SPI 的接口是 Java 核心库的一部分,是由引导类加载器来加载的;SPI 实现的 Java 类一般是由系统类加载器来加载的。引导类加载器是无法找到 SPI 的实现类的,因为它只加载 Java 的核心库。它也不能代理给系统类加载器,因为它是系统类加载器的祖先类加载器。也就是说,类加载器的代理模式无法解决这个问题。 线程上下文类加载器正好解决了这个问题。如果不做任何

doublecj的专栏 1170

为什么需要线程上下文类加载器

文章目录线程上下文类加载器基础案例1:JDK SPI机制问题解决方案案例2:tomcat中spring加载业务类问题解决方案补充:facade模式类加载问题 线程上下文类加载器基础 线程上下文类加载器是一种类加载器传递机制。为什么叫作“线程上下文类加载器”呢,因为这个类加载器保存在线程私有数据里,只要是同一个线程,一旦设置了线程上下文加载器,在线程后续执行过程中就能把这个类加载器取出来用。 线程上下文加载器其实是线程私有数据,跟线程绑定的属性。 站在开发者角度,其他线程都是由main线程,也就是main函数

u013905744的专栏 1173
上一篇: j2ee的资源连接器
下一篇: CAS分析
gaojava
博客等级 码龄20年 20粉丝 28原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值