三大类加载器
1.启动类加载器-BootstrapClassLoader
启动类加载器是由一个系统参数定义:sun.boot.class.path
,其确定加载哪些路径的jar包或class文件。
代码示例:
public static void main(String[] args) {
System.out.println(String.class.getClassLoader());// java中获取不到根类加载器 输出null
String path = System.getProperty("sun.boot.class.path");
for (String p : path.split(";")) {
System.out.println(p);
}
}
输出
null C:\Program Files\jdk\jre\lib\resources.jar C:\Program Files\jdk\jre\lib\rt.jar C:\Program Files\jdk\jre\lib\sunrsasign.jar C:\Program Files\jdk\jre\lib\jsse.jar C:\Program Files\jdk\jre\lib\jce.jar C:\Program Files\jdk\jre\lib\charsets.jar C:\Program Files\jdk\jre\lib\jfr.jar C:\Program Files\jdk\jre\classes
我们可以加在启动参数中通过-Xbootclasspath
参数修改其加载路径
-Xbootclasspath:路径
替换系统指定路径 — 不推荐-Xbootclasspath/a:路径
指定的路径会在jdk核心配置之后搜索 — 可用,推荐-Xbootclasspath/p:路径
指定的路径会在jdk核心配置之前搜索 — 可用,不推荐
a.启动参数加-Xbootclasspath/a:D:/tmp/commons-beanutils-1.9.4.jar
pom中增加依赖
<dependencies>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.4</version>
</dependency>
</dependencies>
同时,复制jar包到D:/tmp/目录下。
public static void main(String[] args) {
System.out.println(BeanUtils.class.getClassLoader());
String path = System.getProperty("sun.boot.class.path");
for (String p : path.split(";")) {
System.out.println(p);
}
}
输出:
null C:\Program Files\jdk\jre\lib\resources.jar C:\Program Files\jdk\jre\lib\rt.jar C:\Program Files\jdk\jre\lib\sunrsasign.jar C:\Program Files\jdk\jre\lib\jsse.jar C:\Program Files\jdk\jre\lib\jce.jar C:\Program Files\jdk\jre\lib\charsets.jar C:\Program Files\jdk\jre\lib\jfr.jar C:\Program Files\jdk\jre\classes D:/tmp/commons-beanutils-1.9.4.jar
可以看到BeanUtils的类加载器时根类加载器(null),同时搜索路径的最后增加了D:/tmp/commons-beanutils-1.9.4.jar
。
b.启动参数增加-Xbootclasspath/p:D:/tmp/commons-beanutils-1.9.4.jar
上面代码不变,只修改启动参数,输出
null D:/tmp/commons-beanutils-1.9.4.jar C:\Program Files\jdk\jre\lib\resources.jar C:\Program Files\jdk\jre\lib\rt.jar C:\Program Files\jdk\jre\lib\sunrsasign.jar C:\Program Files\jdk\jre\lib\jsse.jar C:\Program Files\jdk\jre\lib\jce.jar C:\Program Files\jdk\jre\lib\charsets.jar C:\Program Files\jdk\jre\lib\jfr.jar C:\Program Files\jdk\jre\classes
可见自定义的搜索路径在原搜索路径之前,也就是先搜索自定义路径,再搜索jdk核心路径,这种方式不安全,不推荐使用。
2.扩展类加载器-ExtendClassLoader
根据上小接我们类比应该知道,扩展类加载器是由-Djava.ext.dirs=目录
参数配置的目录确定(替换)。
示例:
启动参数:-Djava.ext.dirs=D:/tmp
public class ClassLoaderDemo {
public static void main(String[] args) {
System.out.println(BeanUtils.class.getClassLoader());
String path = System.getProperty("java.ext.dirs");
for (String p : path.split(";")) {
System.out.println(p);
}
}
}
输出:
sun.misc.Launcher$ExtClassLoader@66d3c617 D:/tmp
3.系统类加载器-AppClassLoader
系统类加载器会加载所有的classpath下的类,类路径目录由参数java.class.path=路径
指定。
加载类路径下的类
public class ClassLoaderDemo {
public static void main(String[] args) {
System.out.println(ClassLoaderDemo.class.getClassLoader());
String path = System.getProperty("java.class.path");
for (String p : path.split(";")) {
System.out.println(p);
}
}
}
输出:
sun.misc.Launcher$AppClassLoader@18b4aac2 C:\Program Files\jdk\jre\lib\charsets.jar …省略 D:\projects\jvm\target\classes C:\Users\leber.m2\repository\commons-beanutils\commons-beanutils\1.9.4\commons-beanutils-1.9.4.jar C:\Users\leber.m2\repository\commons-logging\commons-logging\1.2\commons-logging-1.2.jar C:\Users\leber.m2\repository\commons-collections\commons-collections\3.2.2\commons-collections-3.2.2.jar D:\ideaIU-2021.3.2.win\lib\idea_rt.jar
同理,我们可以在启动参数中配置classpath路径:-Djava.class.path=xxxx
,如同idea启动时一样
类加载器源码
我们知道根类加载器时c++编写又jvm实现,但是扩展类加载器和系统类加载器时java层面实现,其源码我们可以查看(hotspot实现,只有通过反编译)。
在rt.jar包下有个Launcher类,其构造方法中实例化了扩展类加载器和系统类加载器
扩展类加载器
public Launcher() {
Launcher.ExtClassLoader var1;
try {
// 扩展类加载器
var1 = Launcher.ExtClassLoader.getExtClassLoader();
} catch (IOException var10) {
throw new InternalError("Could not create extension class loader", var10);
}
try {
// 系统类加载器,以扩展类加载器作为父加载器
this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
} catch (IOException var9) {
throw new InternalError("Could not create application class loader", var9);
}
Thread.currentThread().setContextClassLoader(this.loader);
// ...
}
扩展类加载器定义在Launcher类中
// 继承至URLClassLoader
static class ExtClassLoader extends URLClassLoader {
// 可知扩展类加载器是单例的
private static volatile Launcher.ExtClassLoader instance;
// 同步获取
public static Launcher.ExtClassLoader getExtClassLoader() throws IOException {
if (instance == null) {
Class var0 = Launcher.ExtClassLoader.class;
synchronized(Launcher.ExtClassLoader.class) {
if (instance == null) {
instance = createExtClassLoader();
}
}
}
return instance;
}
// 创建扩展类加载器
private static Launcher.ExtClassLoader createExtClassLoader() throws IOException {
try {
return (Launcher.ExtClassLoader)AccessController.doPrivileged(new PrivilegedExceptionAction<Launcher.ExtClassLoader>() {
public Launcher.ExtClassLoader run() throws IOException {
// java.ext.dirs参数配置的目录
File[] var1 = Launcher.ExtClassLoader.getExtDirs();
int var2 = var1.length;
// 以后在看
for(int var3 = 0; var3 < var2; ++var3) {
MetaIndex.registerDirectory(var1[var3]);
}
// 实例化
return new Launcher.ExtClassLoader(var1);
}
});
} catch (PrivilegedActionException var1) {
throw (IOException)var1.getException();
}
}
// 构造方法
public ExtClassLoader(File[] var1) throws IOException {
super(getExtURLs(var1), (ClassLoader)null, Launcher.factory);
SharedSecrets.getJavaNetAccess().getURLClassPath(this).initLookupCache(this);
}
系统类加载器AppClassLoader
static class AppClassLoader extends URLClassLoader {
final URLClassPath ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this);
public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {
final String var1 = System.getProperty("java.class.path");
final File[] var2 = var1 == null ? new File[0] : Launcher.getClassPath(var1);
return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<Launcher.AppClassLoader>() {
public Launcher.AppClassLoader run() {
URL[] var1x = var1 == null ? new URL[0] : Launcher.pathToURLs(var2);
// 实例化 以classpath,和扩展类加载器作为参数
return new Launcher.AppClassLoader(var1x, var0);
}
});
}
// 构造方法
AppClassLoader(URL[] var1, ClassLoader var2) {
super(var1, var2, Launcher.factory);
this.ucp.initLookupCache(this);
}
public Class<?> loadClass(String var1, boolean var2) throws ClassNotFoundException {
int var3 = var1.lastIndexOf(46);
if (var3 != -1) {
SecurityManager var4 = System.getSecurityManager();
if (var4 != null) {
var4.checkPackageAccess(var1.substring(0, var3));
}
}
if (this.ucp.knownToNotExist(var1)) {
Class var5 = this.findLoadedClass(var1);
if (var5 != null) {
if (var2) {
this.resolveClass(var5);
}
return var5;
} else {
throw new ClassNotFoundException(var1);
}
} else {
return super.loadClass(var1, var2);
}
}
private void appendToClassPathForInstrumentation(String var1) {
assert Thread.holdsLock(this);
super.addURL(Launcher.getFileURL(new File(var1)));
}
private static AccessControlContext getContext(File[] var0) throws MalformedURLException {
PathPermissions var1 = new PathPermissions(var0);
ProtectionDomain var2 = new ProtectionDomain(new CodeSource(var1.getCodeBase(), (Certificate[])null), var1);
AccessControlContext var3 = new AccessControlContext(new ProtectionDomain[]{var2});
return var3;
}
static {
// 是否可并行加载
ClassLoader.registerAsParallelCapable();
}
}