获取系统类加载器源码分析
ClassLoader.getSystemClassLoader(); //获取系统类加载器
首先可以看下该方法的doc文档,如下:
Returns the system class loader for delegation. This is the default delegation parent for new ClassLoader instances, and is typically the class loader used to start the application.
This method is first invoked early in the runtime's startup sequence, at which point it creates the system class loader and sets it as the context class loader of the invoking Thread.
The default system class loader is an implementation-dependent instance of this class.
If the system property "java.system.class.loader" is defined when this method is first invoked then the value of that property is taken to be the name of a class that will be returned as the system class loader. The class is loaded using the default system class loader and must define a public constructor that takes a single parameter of type ClassLoader which is used as the delegation parent. An instance is then created using this constructor with the default system class loader as the parameter. The resulting class loader is defined to be the system class loader.
该段话的含义主要说明该方法返回的类加载器是作为系统类加载器,用来初始化加载应用的,并且会将调调用该方法的线程上下文的类加载器设置为该类加载器。如果设置了 java.system.class 那么就可以修改系统类加载器,但是必须定义一个公共构造函数,该构造函数接受类装入器类型的单个参数,该参数用作委托父类,其父类就是系统类加载器,该构造方法是由jvm调用的。
进入initSystemClassLoader方法查看
sclSet: 判断系统类加载器是否加载,如果已经加载就为true,反之则为false
scl: 表示系统类加载器
构建Launcher对象,因为Launcher不是开源的此处的代码为反编译后的效果,从其逻辑中可以看到,主要完成三件事:
1:获取扩展类加载器
2:获取系统类加载器,并且将扩展类加载器作为父加载器传递给系统类加载器
3:设置线程上文件类加载器为系统类加载器
根据此处描述可以发现,扩展类加载器是从 System.getProperty("java.ext.dirs") 结果中中加载的,
同理系统类加载器的代码如下:
系统类加载器是从 System.getProperty("java.class.path") 的结果中的目录加载的。
最终会调用 SystemClassLoaderAction的run方法
此处代码先判断 System.getProperty("java.system.class.loader") 是否有值,如果没有值表示系统类加载器就是为 AppClassLoader,否则表示应用指定了系统类加载器
此处主要完成 用户自定义类加载器的实列化并且将AppClassLoader作为父加载器传入构造方法中,最终构建出一个新的用户自定义的系统类加载器,最终将该类加载器设置为当前线程的类加载器。
线程上下文类加载器作用
当前类加载器(Current ClassLoader)
每个类都会使用自己的类加载器(即加载自身的类加载器)去加载器其他的类(指的是所依赖的类),如ClassX引用了ClassY,那么ClassX的类加载器就会去加载ClassY(
前提是ClassY尚未被加载)
线程上下文类加载器(Context ClassLoader)
线程上下文类加载器是从JDK1.2开始引入的,类Thread中的getContextClassLoader()与setContextClassLoader(ClassLoader cl)分别用来获取和设置上下文类加载器。
如果没有通过setContextClassLoader(ClassLoader cl)进行设置的话,线程将继承父线程的上下文类加载器。java应用运行时的初始线程上下文类加载器是系统类加载器。
在线程中运行的代码可以通过该类加载器加载类与资源。
线程上下文类加载器的重要性:
SPI(Service Provider Interface)
父ClassLoader可以使用当前线程Thread.currentThread().getContextClassLoader()所指定的classLoader加载类,这就改变了父ClassLoader不能使用子ClassLoader或者
其他没有直接父子关系的ClassLoader加载类的情况,即改变了双亲委派模型。
线程上下文类加载器就是当前线程的Current ClassLoader。
在双亲委派模型线下,类加载器是由下至上的,即下层的类加载器会委托上层进行加载。但是对于SPI来说,有些接口是Java核心库所提供的,而Java核心库是由启动类加载器
来加载的,而这些接口的实现却来自不同的jar包(厂商提供),java的启动类加载器不会加载其他来源的jar包,这样传统的双亲委托模型就无法满足SPI的要求。而通过给定
线程上下文类加载器就可以通过线程上下文类加载器来完成对于接口实现类的加载。
线程上下文类加载器的一般使用规则
线程上下文类加载器的一般使用(获取 - 使用 - 还原)
//获取原始线程类加载器
ClassLoader classLoader=Thread.currentThread().getContextClassLoader();
try{
//获取需要使用的类加载器
ClassLoader targetLoader=getClassLoader();
//使用获取的类加载
Thread.currentThread.setContetClassLoader(targetLoader);
method();
}finally{
//还原原始的线程类加载器
Thread.currentThread.setContetClassLoader(classLoader);
}
method里面调用了Thread.currentThread().getContextClassLoader()获取当前上下文来加载器做某些事情。
如果一个类是由A加载的,那么这个类依赖的类也是由相同的类加载器加载的(前提是该类没有被加载过)
ContextClassLoader的作用就是为了破坏java类的加载委托机制。
当高层提供了统一的接口让低层去实现,同事又要在高层加载(或实列化)低层的类时,就必须要通过线程上下文类加载器来帮助高层的ClassLoader找到并加载该类。
加载Test5的类加载器为AppClassLoader,其线程类上下文加载器也为AppClassLoader,其父类加载器为扩展类加载器