三大类加载器
1.启动类加载器-BootstrapClassLoader
启动类加载器是由一个系统参数定义:sun.boot.class.path,其确定加载哪些路径的jar包或class文件。
代码示例:
public static void main(String[] args) {System.out.println(String.class.getClassLoader());// java中获取不到根类加载器 输出nullString 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类中
// 继承至URLClassLoaderstatic 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();}}
