深度剖析getcontextclassloader

发布时间:2023-05-23

一、getcontextclassloader的概述

在Java开发中,线程的上下文类加载器(Context ClassLoader)是一个非常关键的概念。每个线程都有一个ClassLoader,由线程的启动器负责初始化。而上下文ClassLoader则是在运行时动态绑定的,用于实现类加载的委托机制。而getcontextclassloader()方法就是用于获取当前线程的上下文ClassLoader。

public static ClassLoader getcontextclassloader(){
   return Thread.currentThread().getContextClassLoader();
}

这个方法是在Thread类中定义的静态方法,使用时不需要创建Thread类的实例。

二、getcontextclassloader的作用

getcontextclassloader()方法主要用于某些扩展框架或插件中,因为扩展框架或插件是由独立的ClassLoader加载的,所以它们可能无法访问Java应用程序的类。而上下文ClassLoader的概念,让这类框架或插件能够通过getParent()方法动态获取Java应用程序的ClassLoader,并随之继续向上委托ClassLoader,从而解决了访问问题。 另外,在一些不同的OSGI模块化架构中,每个模块都有自己的ClassLoader,在这种情况下,getcontextclassloader()方法能够非常好地解决模块之间的调用问题。

三、getcontextclassloader的具体实现

在Java中,ClassLoader的继承关系呈现树状结构,从根加载器(Boot ClassLoader)开始,向下分为三层:①Bootstrap ClassLoader;②Extension ClassLoader;③System ClassLoader。其中启动类加载器(Bootstrap ClassLoader)是Java虚拟机实现的一部分,不存在对应的Java类,因此不存在对应的ClassLoader实例对象。这意味着,它并不是一个ClassLoader对象。 在Java应用系统启动后,bootstrap class loader会读取jar文件,并将jar文件中的class文件存放在运行时数据区的方法区中(内存中)。当一个新的class需要被加载时,JVM会先请求bootstrap class loader去加载。如果加载不成功,再依次由extensions class loader、system class loader去进行加载。 在Java中,每个线程都拥有一个上下文ClassLoader,而这个上下文ClassLoader会继承自线程启动时所在的ClassLoader,除非你设置了新的上下文ClassLoader。 对于getcontextclassloader()方法的实现,我们需要明确两个概念:当前线程和当前线程上下文ClassLoader。Thread.currentThread()方法返回当前线程,getContextClassLoader()方法返回当前线程上下文ClassLoader。所以getcontextclassloader()方法的实现就是通过Thread.currentThread().getContextClassLoader()获取当前线程上下文ClassLoader。

四、getcontextclassloader的示例代码

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class ContextClassLoaderDemo {
    public static void main(String[] args) {
        Thread.currentThread().setContextClassLoader(ContextClassLoaderDemo.class.getClassLoader().getParent());
        //获取当前线程上下文ClassLoader
        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        System.out.println("contextClassLoader = " + contextClassLoader);
        //使用当前线程上下文ClassLoader加载配置文件
        InputStream inputStream = contextClassLoader.getResourceAsStream("test.properties");
        //读取配置文件
        Properties properties = new Properties();
        try {
            properties.load(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
        String value = properties.getProperty("key");
        System.out.println(value);
    }
}

通过这段代码,我们可以看到,在main方法中先设置当前线程的上下文ClassLoader,然后通过getcontextclassloader()方法获取上下文ClassLoader,并使用它加载配置文件。

五、getcontextclassloader和线程上下文ClassLoader的关系

对于线程上下文ClassLoader,我们需要注意以下几点:

  1. 如果将当前线程的上下文ClassLoader设置为null,那么默认情况下,它的上下文ClassLoader是父线程的上下文ClassLoader;
  2. Java应用程序的ClassLoader默认是系统ClassLoader,也就是AppClassLoader;
  3. Java应用程序的系统ClassLoader,并不是来自于AppClassLoader,而是来自于根ClassLoader的反射调用,这意味着我们可以通过反射获取系统ClassLoader;
  4. 线程上下文ClassLoader的设定是为了解决一些类加载问题,如果没有明确的需求,应该避免直接操作线程上下文ClassLoader。

六、小结

getcontextclassloader()方法的作用是获取当前线程的上下文ClassLoader。在Java开发中,ClassLoader的继承关系呈现树状结构,而每个线程都有一个上下文ClassLoader,用于实现类加载的委托机制。getcontextclassloader()方法通常用于一些扩展框架或插件中,以及在OSGI模块化架构中,用于解决模块之间的调用问题。需要注意的是,线程上下文ClassLoader的设定是为了解决一些类加载问题,如果没有明确的需求,应该避免直接操作线程上下文ClassLoader。