深入理解ContextClassLoader加载器

 更新时间:2023年10月07日 10:08:25   作者:西魏陶渊明  
这篇文章主要介绍了深入理解ContextClassLoader加载器,Thread context class loader存在的目的主要是为了解决parent delegation机制下无法干净的解决的问题,需要的朋友可以参考下

ContextClassLoader

Thread.currentThread().getContextClassLoader(); - 从方法名字来看,应该是获取当前上下文的类加载器 搞清楚这个问题, 当你在出现资源加载不到的时候就很容器解决

那么问题来了,为什么要这样设计? 解决了什么样的设计问题? 解决了什么样的开发问题? 

  • 解决委派双亲加载模式的缺点
  • 实现了JNDI等
  • 解决开发中,文件加载不到的异常
Thread.currentThread().getContextClassLoader();
this.getClass().getClassLoader();

类加载器之前一直迷惑,终于这个问题在一篇博客的回答中,找到了清晰易懂的解释

原文是这样的:

Thread context class loader存在的目的主要是为了解决parent delegation机制下无法干净的解决的问题。

假如有下述委派链: ClassLoader A -> System class loader -> Extension class loader -> Bootstrap class loader

那么委派链左边的ClassLoader就可以很自然的使用右边的ClassLoader所加载的类。

但如果情况要反过来,是右边的ClassLoader所加载的代码需要反过来去找委派链靠左边的ClassLoader去加载东西怎么办呢?没辙,parent delegation是单向的,没办法反过来从右边找左边.*

类加载器的委派双亲模式?

就是说当我们this.getClass().getClassLoader();可以获取到所有已经加载过的文件, 但是 System class loader -> Extension class loader -> Bootstrap class loader 就获取不到 ClassLoader A 能加载到的信息,那么怎么办呢? 于是,Thread就把当前的类加载器,给保存下来了,其他加载器,需要的时候,就把当前线程的加载器,获取到.

那么什么场景下,会遇到这种情况那,当通常发生在有些JVM核心代码必须动态加载由应用程序开发人员提供的资源时eg:

JNDI

JNDI(Java Naming and Directory Interface,Java命名和目录接口)是SUN公司提供的一种标准的Java命名系统接口,JNDI提供统一的客户端API,通过不同的访问提供者接口

在系统中,要调用开发者的资源,此时就遇到了这种情况

JAX和rt.jar 因为是两个加载器加载的 那么BootStrap需要加载Ext的资源,怎么办? 这不是与委托机制相反了吗? 所以就不能只依赖委派双亲模式,那么怎么做

然后我们看一波,Thread源码的注释,提供了,获取上下文加载器方法 Thread.currentThread().getContextClassLoader()

Thread

 /* The context ClassLoader for this thread */
    private ClassLoader contextClassLoader;

问题:

项目中需要加载应用配置,加载不到,需要用ClassPathResource

问题同上,父加载器要加载应用配置,因此需要调用上下文加载器

代码块分析

//获取文件绝对地址,并不是jar里面的文件路径
URL resource = BlmSignature.class.getClassLoader().getResource(keystoreFilePath);
String path = resource.getPath();
//当发布到线上只发布jar文件, 所以就会报异常,找不到
FileInputStream keystoreinputStream=new FileInputStream(path);
//正确的做法是,获取到jar包里面的文件,需要注意类加载是否能加载到的问题,
//1. Spring工具
keystorePath = "classpath:"+keystoreFilePath;
ClassPathResource classPathResource = new ClassPathResource(keystorePath);
InputStream keystoreinputStream = classPathResource.getInputStream();
//使用类加载器加载classpath里面的
//指定Thread.currentThread().getContextClassLoader();加载器
SmileClassPathResource smileClassPathResource = new SmileClassPathResource(keystoreFilePath);
InputStream  keystoreinputStream=smileClassPathResource.getInputStream();

SmileClassPathResource源码

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
/**
 * @Package: org.smileframework.tool.io
 * @Description: 加载配置文件
 * @author: liuxin
 * @date: 2017/12/19 上午9:23
 */
public class SmileClassPathResource {
    private final String path;
    private ClassLoader classLoader;
    public SmileClassPathResource(String name) {
        this(name, getDefaultClassLoader());
    }
    public SmileClassPathResource(String name, ClassLoader classLoader) {
        this.path = name;
        this.classLoader = classLoader;
    }
    public static ClassLoader getDefaultClassLoader() {
        ClassLoader cl = null;
        try {
            cl = Thread.currentThread().getContextClassLoader();
        } catch (Throwable var3) {
            ;
        }
        if(cl == null) {
            cl = ClassUtils.class.getClassLoader();
            if(cl == null) {
                try {
                    cl = ClassLoader.getSystemClassLoader();
                } catch (Throwable var2) {
                    ;
                }
            }
        }
        return cl;
    }
    public InputStream getInputStream() {
        InputStream is;
        if (this.classLoader != null) {
            is = this.classLoader.getResourceAsStream(this.path);
        } else {//当还是加载不到,调用上层加载器
            is = ClassLoader.getSystemResourceAsStream(this.path);
        }
        if (is == null) {
            throw new RuntimeException(path + " cannot be opened because it does not exist");
        } else {
            return is;
        }
    }
    public String getResourceStreamAsString() {
        InputStream is = getInputStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        StringBuilder sb = new StringBuilder();
        String line = null;
        try {
            while ((line = reader.readLine()) != null) {
                sb.append(line + "\n");
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return sb.toString();
    }
    public static void main(String[] args) {
        SmileClassPathResource classPathResource = new SmileClassPathResource("logback.xml");
        System.out.println(classPathResource.getResourceStreamAsString());
    }
}

到此这篇关于深入理解ContextClassLoader加载器的文章就介绍到这了,更多相关ContextClassLoader加载器内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 简单学习Java抽象类要点及实例

    简单学习Java抽象类要点及实例

    这篇文章主要介绍了Java抽象类要点及实例,有需要的朋友可以参考一下
    2014-01-01
  • java文本处理之计算文本句子数

    java文本处理之计算文本句子数

    这篇文章主要为大家详细介绍了java文本处理之计算文本句子数,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-08-08
  • Springboot整个Quartz实现动态定时任务的示例代码

    Springboot整个Quartz实现动态定时任务的示例代码

    这篇文章主要介绍了Springboot整个Quartz实现动态定时任务的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-09-09
  • JAVA 实现二叉树(链式存储结构)

    JAVA 实现二叉树(链式存储结构)

    本篇文章主要介绍用JAVA 实现二叉树,并提供实例.对二叉树数据结构很好的学习实践,有需要的朋友可以参考下
    2016-07-07
  • 聊聊@RequestMapping和@GetMapping @PostMapping的区别

    聊聊@RequestMapping和@GetMapping @PostMapping的区别

    这篇文章主要介绍了@RequestMapping和@GetMapping及@PostMapping的区别,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08
  • SpringBoot @ModelAttribute使用场景分析

    SpringBoot @ModelAttribute使用场景分析

    这篇文章主要介绍了SpringBoot @ModelAttribute使用场景分析,文中通过实例代码图文相结合给大家介绍的非常详细,需要的朋友可以参考下
    2021-08-08
  • Windows安装Maven并配置环境的详细步骤

    Windows安装Maven并配置环境的详细步骤

    Maven是一个非常流行的构建和项目管理工具,用于Java开发,它提供了一个强大的依赖管理系统和一系列标准化的构建生命周期,本文将指导您如何在Windows操作系统上安装和配置Maven,需要的朋友可以参考下
    2023-05-05
  • Java锁机制Lock用法示例

    Java锁机制Lock用法示例

    这篇文章主要介绍了Java锁机制Lock用法,结合具体实例形式分析了Java锁机制的相关上锁、释放锁、隐式锁、显式锁等概念与使用技巧,需要的朋友可以参考下
    2018-08-08
  • SpringBoot项目打包war包时无法运行问题的解决方式

    SpringBoot项目打包war包时无法运行问题的解决方式

    在开发工程中,使用启动类启动能够正常启动并测试,下面这篇文章主要给大家介绍了关于SpringBoot项目打包war包时无法运行问题的解决方式,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-06-06
  • 利用JSONObject.toJSONString()包含或排除指定的属性

    利用JSONObject.toJSONString()包含或排除指定的属性

    这篇文章主要介绍了利用JSONObject.toJSONString()包含或排除指定的属性,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03

最新评论