【JVM类加载】线程上下⽂的类加载器及使⽤模式ServiceLoader(服务加载
器)在S。。。
线程上下⽂的类加载器(setContextClassLoader)
当前类加载器(Current ClassLoader)
每个类都会使⽤⾃⼰的类加载器(即加载⾃⾝的类加载器)来去加载其他类(指的是是所依赖的类),如果classX引⽤ClassY,那么ClassX的类加载器就会去加载ClassY(前提是ClassY尚未加载)
线程上下⽂类加载器(Context ClassLoader)
线程上下⽂类加载器是从JDK1.2开始引⼊的,类Thread中的getContextClassLoader()与setContextClassLoader(ClassLoader cl)分别⽤来获取和设置上下⽂类加载器
线程上下⽂类加载器的重要性
SPI :(Service Provider Interface)服务提供者接⼝如:jdbc 是⽤来声明接⼝制定⼀些标准(仅仅通过双亲委托来加载类时 ⽗加载器⽆法看到⼦类加载器(命名空间) 但是⽗加载器加载 如 根加载器加载⼀些rt.jar包⾥的⼀些内容如jdbc定义的接⼝,需要调外部别⼈实现的⼀些类时就⽆法访问到)
⽗类ClassLoader可以使⽤当前线程Thread.currentThread().getContextClassLoader()所指定的classloader加载对应的类放到内存中,供⽗类加载器加载的类使⽤。这就:改变了⽗classLoader不能使⽤⼦ClassLoader或是其他没有直接⽗⼦关系的classLoader加载的类的情况。即改变了双亲委托模型**
线程上下⽂类加载器就是当前线程的Current Classloader。
在双亲委托模型下,类加载是由下⾄上的,即下层的类加载器会委托上层的加载。但是对于SPI来说,有些接⼝是java核⼼库所提供的,⽽java核⼼库是由启动类加载器来加载的,
⽽这些接⼝的实现去来⾃于不同的jar包(⼚商提供)java的启动类加载器是不会加载其他来源的jar包,这样传统的双亲委托模型就⽆法满⾜SPI的要求,
⽽通过给当前线程设置上下⽂类加载器,就可以由设置的上下⽂类加载器来实现对于接⼝实现类的加载
public class MyTest24 {
mysql下载jar包public static void main(String[] args){
System.out.println(Thread.currentThread().getContextClassLoader());//AppClassLoader
System.out.println(ClassLoader());//null Thred位于lang包
}
}
如果没有设置线程上下⽂类加载器,该线程上下⽂类加载器默认设置为系统类加载器
public class MyTest25 implements Runnable {
private Thread thread;
public MyTest25(){
this.thread =new Thread(this);
thread.start();
}
@Override
public void run(){
ClassLoader classLoader =ContextClassLoader();
this.thread.setContextClassLoader(classLoader);
System.out.println("Class: "+ Class());
System.out.println("parent: "+ Parent().getClass());
}
public static void main(String[] args){
new MyTest25();
}
}
/*
Class: class sun.misc.Launcher$AppClassLoader //如果没有设置线程上下⽂类加载器默认设置为系统类加载器具体在getLauncher 讲解⾥ parent: class sun.misc.Launcher$ExtClassLoader //系统类加载器的⽗类只是打破加载规则并不打破包含规则
*/
线程上下⽂类加载器的⼀般使⽤模式
线程上下⽂类加载器的⼀般使⽤模式(获取->使⽤->还原)
//获取
ClassLoader classLoader = ContextClassLoader();
try{
//设置要使⽤的类加载器
Thread.currentThread.setContextClassLoader(cls);
//使⽤
MyMethod();
}finally{
//如果不还原使⽤的就是设置的类加载器
//还原
Thread.currentThread.setContextClassLoader(classLoader);
}
上例中:
MyMethod⾥⾯则调⽤了Thread.currentThread().getContextClassLoader(),获取当前线程的上下⽂类加载器做某些事情MyMethod()。
如果⼀个类由类加载器A加载,那么这个类的依赖类也是由相同的类加载器加载的(如果该依赖类之前没有被加载过的话) 如:启动类加载器扫描不到系统类加载器的内容 上下⽂类加载器就是为了解决这个问题
ContextClassLoader的作⽤就是为了破坏Java的类加载委托机制(如上)
当⾼层提供了统⼀的接⼝让低层去实现,同时⼜要在⾼层加载(或实例化)底层的类时,就必须要通过线程上下⽂类加载器来帮助⾼层的ClassLoader到并加载该类(上下⽂类加载器就是为了解决⽗类加载器加载的类⽆法看到⼦类加载器加载的类)
我们可以直接使⽤getClassLoader获取系统类加载器去加载对应的类为什么还要使⽤上下⽂类加载器?
1.⽅便,执⾏的任何代码都在线程中 我们可以随时取出来对应的上下⽂类加载器使⽤
2.解决 ⾼层加载底层类问题
3.有特定的情况,当前的线程类加载器 不⼀定是系统类加载器 此时不能加载classpath下的.class⽂件
import java.sql.Driver;
import java.util.Iterator;
import java.util.ServiceLoader;
public class MyTest26 {
public static void main(String[] args){
//Driver驱动连接规范的⼀些信息
//load()等价与load(Class<S> service, ClassLoader loader)
//load(需要加载服务的class,Thread.currentThread().getContextClassLoader())
ServiceLoader<Driver> loader = ServiceLoader.load(Driver.class);
Iterator<Driver> iterator = loader.iterator();
while(iterator.hasNext()){
Driver driver = ();
System.out.println("driver: "+Class()+",loader: "+Class().getClassLoader());
}
System.out.println("当前线程上下⽂类加载器: "+Thread.currentThread().getContextClassLoader());
System.out.println("ServiceLoader的类加载器:"+Class().getClassLoader());
}
}
/*
为什么仅仅是Driver.class 这个接⼝就能到以下两个Driver实现类
服务提供者将提供者(供应商)的程序配置⽂件放在资源⽬录META-INF/services⽬录**下当中。
该⽂件的名称(如mysql 加载java.sql.Driver⽂件类加载器就可以加载这个⽂件⾥⾯每⼀⾏的⼆进制名(类名的路径此时对应的类就会被加载))
driver: class com.alibaba.druid.proxy.DruidDriver,loader: sun.misc.Launcher$AppClassLoader@18b4aac2 所有maven下载的jar都位于classpath下driver: class com.k.MockDriver,loader: sun.misc.Launcher$AppClassLoader@18b4aac2
driver: sql.cj.jdbc.Driver,loader: sun.misc.Launcher$AppClassLoader@18b4aac2
---
当前线程上下⽂类加载器: sun.misc.Launcher$AppClassLoader@18b4aac2 未设置默认为系统类加载器
ServiceLoader的类加载器:null --ServiceLoader位于rt.jar包下
*/
ServiceLoader(服务加载器)源码分析(jdk1.6之后)
1.doc官⽅⽂档ServiceLoader(服务加载器)在SPI中的重要作⽤分析
extends Object
implements 12
如果在多个配置⽂件中出现了同⼀个具体提供者类的名字的话,或在相同的配置⽂件中名字出现了⼀次以上,重复项将被忽略。配置⽂
CodecSet类创建并保存⼀个服务实例 如上如果是两个就是加载保存两个实例
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论