【深入淺出-JVM】(76):classloader
- 2019 年 10 月 3 日
- 筆記
方法
- public Class<?> loadClass(String name) throws ClassNotFoundException
通過類名發揮這個類的Class實例 - protected final Class<?> defineClass(byte[] b,int off,int len)
根據給定的位元組碼流 b,off 和 len 參數表示實際的 class 資訊在byte 數組中的位置和長度,其中 byte 數組 b是 classloader 從外部獲取的 - protected Class<?> findClass(String name)throws ClassNotFoundException
查看一個類 -
protected final Class<?> findLoadedClass(String name)
分類
- BootStrap ClassLoader
- Extension ClassLoader
-
AppClassLoader
啟動類載入器負責載入系統的核心類(rt.jar的java類),擴展類載入器載入 %JAVA_HOME/lib/ext/*.jar中的類,應用類載入器用於載入用戶類 (classpath),自定義類載入器載入一些特殊路徑的類(自定義classloader)雙親委託
- 當前 classloader 首先從自己已經載入的類中查詢是否此類已經載入,如果已經載入了則直接返回原來已經載入的類
- 當前 classloader 的快取中沒有找到被載入的類的時候,委託父類載入器去載入,父類載入器採用同樣的策略,首先查下自己的快取,然後委託父類去載入,一直到 bootstrap classloader
- 當所有的父類載入器都沒有載入的時候,再由當前的類載入器載入,並將其放入自己的快取中,下次請求的時候直接返回
- 一直循環重複
作用
- 各個類載入器的基礎類統一
jar -cvf test.jar HelloLoader.class 把class打包成jar
Extension ClassLoader
例子:在 ext 路徑下放一個自己 jar 包並載入
package com.mousycoder.server; public class HelloWorld { public static void main(String[] args) { System.out.println("Hello World!"); } }
idea 通過 structs->artifacts->jar 然後 build-> build artifacts->build 生成 helloworld.jar
放到 /Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/jre/lib/ext
package com.mousycoder.mycode.thinking_in_jvm; import java.lang.reflect.Method; /** * @version 1.0 * @author: mousycoder * @date: 2019-09-06 10:35 */ public class ExtClassLoader { public static void main(String[] args) throws ClassNotFoundException { System.out.println(System.getProperty("java.ext.dirs")); Class<?> helloClass = Class.forName("com.mousycoder.server.HelloWorld"); System.out.println(helloClass.getClassLoader()); } }
輸出
/Users/mousycoder/Library/Java/Extensions:/Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/jre/lib/ext:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java sun.misc.Launcher$ExtClassLoader@610455d6
可以看出是 ExtClassLoader 載入 java.ext.dirs 目錄
自定義類載入器
package com.mousycoder.mycode.thinking_in_jvm; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; /** * @version 1.0 * @author: mousycoder * @date: 2019-09-06 11:13 */ public class MyClassLoader extends ClassLoader { private final static Path DEFAULT_CLASS_PATH = Paths.get("","/Users/mousycoder/My"); private final Path classDir; public MyClassLoader(){ super(); this.classDir = DEFAULT_CLASS_PATH; } public MyClassLoader(String classDir){ super(); this.classDir = Paths.get(classDir); } public MyClassLoader(String classDir, ClassLoader parent){ super(parent); this.classDir = Paths.get(classDir); } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { try { byte[] classBytes = this.readClassBytes(name); if (null == classBytes || 0 == classBytes.length){ throw new ClassNotFoundException("can not load the class" + name); } return this.defineClass(name,classBytes,0,classBytes.length); } catch (IOException e){ e.printStackTrace(); } return null; } private byte[] readClassBytes(String name) throws ClassNotFoundException, IOException { String classPath = name.replace(".","/"); Path classFullPath = classDir.resolve( "HelloWorld1.class"); if (!classFullPath.toFile().exists()){ throw new ClassNotFoundException("The class" + name + "mpt found"); } try (ByteArrayOutputStream baos = new ByteArrayOutputStream()){ Files.copy(classFullPath,baos); return baos.toByteArray(); } catch (IOException e){ throw new ClassNotFoundException("load the class " + name + "occur error",e); } } @Override public String toString() { return "My ClassLoader"; } } package com.mousycoder.mycode.thinking_in_jvm; /** * @version 1.0 * @author: mousycoder * @date: 2019-09-06 11:34 */ public class MyClassLoaderTest { public static void main(String[] args) throws ClassNotFoundException { MyClassLoader classLoader = new MyClassLoader(); Class<?> class1 = classLoader.loadClass("com.mousycoder.mycode.thinking_in_jvm.HelloWorld1"); System.out.println(class1.getClassLoader()); } } package com.mousycoder.mycode.thinking_in_jvm; /** * @version 1.0 * @author: mousycoder * @date: 2019-09-06 11:46 */ public class HelloWorld1 { public static void main(String[] args) { System.out.println("Hello world1 "); } }
把helloword1變成class放到/Users/mousycoder/My目錄下即可
輸出 My ClassLoader 代表 自定義類載入器載入了該類
上下文類載入器
作用
打破雙親委託機制,讓上層父類載入器可以使用子類的載入器載入對象,比如Spi中的介面類在系統載入器中,但是實現類在應用載入器中
Tomcat 類載入器
目的
- 保證每個應用的類庫獨立隔離(即使同限定名不同版本的)
- 保證相同類庫相同版本的類庫共享
- 保證容器自身的類庫和程式獨立
載入順序
- bootstrap 引導類載入器
- system 系統類載入器
- 應用類載入器 WEB-INF/classes
- 應用類載入器 WEB-INF/lib
- common 類載入器 CATALINA/lib