【java 基础】闲话 ClassLoader 和 资源读取

接上一篇:【java 基础】闲话 ClassLoader 和 SPI

ClassLoader再探

由上一篇我们知道,不同的 ClassLoader加载的资源范围不同。除了最常见的 java 自己实现的三个类加载器以外,其实还有很多别的加载器。比如spring、 springboot、以及早些年常用的 tomcat,都有自己的加载器。

tomcat的类加载器

tomcat为什么要自定义类加载器,主要有以下几个原因:

  • 作为 web 容器,可以同时运行多个应用。而不同的应用可能使用相同的jar包,但可能版本不同,为了防止出现类冲突,所以必须要对不同的应用进行隔离,方法便是不同的应用使用不同的WebAppClassLoader 实例。
  • 在war包中,类的存储结构是放在/WEB-INF/classes 和/WEB-INF/lib 中,所以类的扫描路径也会不同,需要自定义扫描。
  • 对于常用的包,比如 log4j 之类,如果在每个 classLoader 中都加载一次,是对资源的浪费,所以 tomcat 还存在一个共享的 ShareClassLoader作为WebAppClassLoader 的父加载器。

spring 的类加载器

spring也有自己的加载器:OverridingClassLoader,与双亲委派不同,OverridingClassLoader是自己先尝试加载,在无法加载到的情况下才会启用其父加载器(双亲委派)。即 spring 优先使用自己定义的类。

springboot 的类加载器

我们知道springboot FAT jar 的方式将依赖包都打包到了 BOOT-INF/lib 下,这就导致 java 普通的类加载器无法加载到jar 包中的 jar 包,所以 springboot实现了LaunchedURLClassLoader类加载器来实现自定义加载。

正确获取ClassLoader

通过以上描述我们大概说明了一下类加载器的一些自定义应用。而我们实际使用中,我们怎么获取正确的 ClassLoader呢?
常用的有两种方式获取 ClassLoader

  • Thread.currentThread().getContextClassLoader();
  • this.getClass().getClassLoader();

获取当前线程的ContextClassLoader

首先说明一下Thread.currentThread().getContextClassLoader():
在初始化 Thread 时可以指定我们要使用的 ClassLoader,默认情况下是内置的AppClassLoader(在 jvm 启动时指定),而比如 tomcat 等在会在每个应用启动时,初始化自己的 ClassLoader,并且在子线程初始化时会继承父线程的 ClassLoader(Thread 的默认行为)。

加载当前类的ClassLoader

而 this.getClass().getClassLoader()则是获取加载此类的 ClassLoader。每个类对象都会保存加载它自身的 classLoader。还有一个ClassName.class.getClassLoader(); 区别于this.getClass().getClassLoader(),用于静态方法中。

不常用的获取 ClassLoader的方法

ClassLoader.getSystemClassLoader(); //直接获取内置的AppClassLoader
ClassLoader.getPlatformClassLoader(); //直接获取内置的ExtClassLoader

使用ClassLoader的读取资源

通过以上的内容,我们说明了 ClassLoader 的扫描范围,以及如何正确的获取自己想要的 ClassLoader,搞明白了这些内容后,就可以开始应用 ClassLoader 进行资源读取了。

ClassLoader 有几个常用的资源加载方法

  • public URL getResource(String name);
    可以获取到相对于classpath根路径下的资源定位信息,通过 openStream 方法可以开启流读取。如果匹配到多个值,则返回第一个。
  • public Enumeration getResources(String name);
    同getResource,只是会返回所有匹配到的资源。
  • public InputStream getResourceAsStream(String name);
    同getResource,只是会返回直接返回流。
  • public URL getSystemResource(String name);
    不常用,直接获取 AppClassLoader,然后调用getResource(name)方法。范围更小
  • public InputStream getSystemResourceAsStream(String name);
    同getSystemResource方法,只是直接返回流。

注意⚠️:name 不能以/开头。

Class 的资源加载方法

  • public URL getResource(String name);
    如果 name 以/开头,则行为同 ClassLoader 的 getResource(name)方法。如果不以/开头,则路径是相对于当前类所在目录。
  • public InputStream getResourceAsStream(String name);
    同Class.getResource方法,只是直接返回流。

ok,结束收工!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值