自定義ClassLoader的實現

  • 2019 年 12 月 5 日
  • 筆記

ClassLoader 是 JVM 結構中很重要的一塊,也可以說是整個 JVM 運行機制的入口,程式通過 ClassLoader 將編譯好的位元組碼文件載入到記憶體中,生成 Class,進而創建對象,之後才能進行各種運算、解析,最終生成機器碼提交到作業系統中。

JVM 結構如下圖所示。

ClassLoader 有 4 種具體的分類:

1、BootstrapClassLoader

2、ExtClassLoader

3、AppClassLoader

4、自定義 ClassLoader

BootstrapClassLoader 用來載入 Java 的核心類庫,即存放在 java.* 包中的位元組碼文件,如 java.util.List、java.io.InputStream、java.lang.Integer 等。

ExtClassLoader 用來載入 Java 的擴展類庫,即存放在 javax.* 包中的位元組碼文件,如 javax.sql.DataSource、javax.net.SocketFactory 等。

AppClassLoader 用來載入當前程式所在目錄的類,即開發者自己編寫的 Java 文件對應的位元組碼文件。

自定義的 ClassLoader 指開發者根據具體需求編寫的類載入器,可以實現訂製化載入。

接下來我們來實現一個自定義的 ClassLoader,首先是準備工作,ClassLoader 的作用是將位元組碼文件載入到記憶體中,所有你得先有位元組碼文件。

1、創建一個 Java 文件。

public class HelloWorld{    public HelloWorld(){      System.out.println("創建了HelloWorld對象");    }  }

2、將其編譯成位元組碼文件。

javac HelloWorld.java

有了位元組碼文件,接下來就可以編寫自定義 ClassLoader,並通過其載入位元組碼文件了,ClassLoader 具體的工作流程主要是通過兩個方法完成類載入的,分別是 findClass 和 defineClass。

findClass 根據路徑載入位元組碼文件,然後交給 defineClass,再把位元組碼轉化為 Class。自定義 ClassLoader 需要開發者對 findClass 方法進行重寫,完成載入位元組碼文件的操作,之後再將位元組數據傳給 ClassLoader 的 defineClass 方法即可,根據這個思路,我們來實現程式碼。

3、創建自定義 ClassLoader,並繼承 ClassLoader。

import java.io.*;    public class MyClassLoader extends ClassLoader {      private String path;        public MyClassLoader(String path) {          this.path = path;      }        @Override      protected Class<?> findClass(String name) throws ClassNotFoundException {          String classPath = path+name+".class";          InputStream inputStream = null;          ByteArrayOutputStream outputStream = null;          try {              inputStream = new FileInputStream(classPath);              outputStream = new ByteArrayOutputStream();              int temp = 0;              while((temp = inputStream.read()) != -1){                  outputStream.write(temp);              }          } catch (FileNotFoundException e) {              e.printStackTrace();          } catch (IOException e) {              e.printStackTrace();          }finally {              try {                  outputStream.close();                  inputStream.close();              } catch (IOException e) {                  e.printStackTrace();              }          }          byte[] bytes = outputStream.toByteArray();          return defineClass(name,bytes,0,bytes.length);      }  }

在 findClass 方法中,我們通過 IO 流讀取本地編譯好的位元組碼文件,生成位元組數組,再將位元組數組傳給 ClassLoader 的 defineClass 方法即可。

4、進行測試,實例化 MyClassLoader,並通過該實例化對象載入位元組碼文件獲取 Class 對象。

public class Test {      public static void main(String[] args) {          MyClassLoader myClassLoader = new MyClassLoader("/Users/southwind/myjava/");          try {              Class clazz = myClassLoader.findClass("HelloWorld");              System.out.println(clazz);              System.out.println(clazz.getConstructor().newInstance());          } catch (Exception e) {              e.printStackTrace();          }      }  }

5、運行結果如下圖所示。

HelloWorld 位元組碼文件載入成功,以上就是自定義 ClassLoader 的用法,你學會了嗎?