日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

URLClassLoader加載Class時的類初始化問題

作者:xuguofeng2016 更新時間: 2022-07-22 編程語言

本文將通過一個示例代碼說明URLClassLoader加載Class時的幾個問題:

  1. Class.forName(…)方法確實是使用傳遞的classLoader加載目標類
  2. initialize參數傳遞false不會初始化靜態資源
  3. 使用classLoader加載的類,使用了其余的類,jvm仍然會使用classLoader加載這個類



遇到的問題

之前在做maven插件(feign接口自動生成工具)的時候,需要掃描maven工程類,但是mvn在運行時又不會加載工程類,好在maven插件中可以通過project將工程類和依賴jar獲取到,所以可以自己編寫代碼把這些類和jar加載進來。

在java1.8版本,可以先獲取到當前系統類加載器(ClassLoader.getSystemClassLoader()方式獲取),此ClassLoader是一個URLClassLoader實例,所以可以通過反射調用addURL方法將工程類和依賴jar加載到當前jvm中,大概的方式是這樣的:

File file = new File(jar);

Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
method.setAccessible(true);

URL url1 = new URL("file:" + file.getAbsolutePath());
// 設置類加載器
URLClassLoader classLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
// 將當前類路徑加入到類加載器中
method.invoke(classLoader, url1);

但是之后使用java11版本時,發現系統類加載器不再是URLClassLoader實例,強制轉型時就失敗了,而且反射調用非public方法也不再允許,所以只能另外尋找解決方案。于是想到了在程序里面創建一個URLClassLoader實例,通過這個URLClassLoader加載工程類和依賴jar,大概思路是這樣的:

  • 使用URLClassLoader加載類庫
  • 使用Class.forName(“className”, initialize, classLoader)方法將具體類加載到jvm。注意這里的initialize要為false,如果傳遞true,會對靜態代碼進行初始化,可能出錯。classLoader就是我們前面使用的URLClassLoader

下面我們使用一個示例程序證明一下以下幾個問題:

  1. Class.forName(…)方法確實是使用傳遞的classLoader加載目標類
  2. initialize參數傳遞false不會初始化靜態資源
  3. 使用classLoader加載的類,使用了其余的類,jvm仍然會使用classLoader加載這個類

示例代碼

都是一些最簡單的代碼,代碼不做過多介紹。

Test1

public class Test1 {

  public static String name = initName();

  static {
    System.out.println("Test1.static code");
    System.out.println("Test1.name:" + name);
  }

  private static String initName() {
    System.out.println("Test1.initName()");
    return "admin";
  }

  @TestAnnotation(arg = 12)
  public void test1() {
    System.out.println("Test1.test1()");
    Test2 test2 = new Test2();
    test2.test2();
  }
}

Test2

public class Test2 {

  public void test2() {
    System.out.println("Test2.test2()");
  }
}

TestAnnotation

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TestAnnotation {

  int arg();
}

TestUrlClassLoader

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Constructor;
import java.net.URL;
import java.net.URLClassLoader;

public class TestUrlClassLoader {

  public static void main(String[] args) throws Exception {

    File file = new File(args[0]);

    // 使用自定義的MyUrlClassLoader加載外部jar文件
    URLClassLoader urlClassLoader = new MyUrlClassLoader(new URL[]{file.toURI().toURL()});

    // 使用MyUrlClassLoader加載Test1
    // 不初始化
    Class<?> clazz = Class.forName("Test1", false, urlClassLoader);
    // 獲取test1方法
    Method method = clazz.getMethod("test1");

    try {
      // 這里會出錯,因為當前系統類加載器沒有這個類
      method.getAnnotation(TestAnnotation.class);
    } catch (Throwable e) {
      e.printStackTrace();
    }

    // 使用反射方式獲取注解值
    Class<Annotation> testAnnotation =
        (Class<Annotation>) Class.forName("TestAnnotation", false, urlClassLoader);
    Annotation annotation = method.getAnnotation(testAnnotation);
    Method arg = testAnnotation.getDeclaredMethod("arg");
    Object invoke = arg.invoke(annotation);
    System.out.println("TestAnnotation.value:" + invoke);

    // 以下創建Test1對象,這里一定會做初始化
    // Constructor constructor = clazz.getConstructor();
    // Object o = constructor.newInstance();
    // method.invoke(o);
  }

  public static class MyUrlClassLoader extends URLClassLoader {

    public MyUrlClassLoader(URL[] urls) {
      super(urls);
    }

    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
      System.out.println("MyURLClassLoader.loadClass:" + name);
      return super.loadClass(name);
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
      System.out.println("MyURLClassLoader.findClass:" + name);
      return super.findClass(name);
    }
  }
}

運行示例

編譯文件

[root@docker-0 test-urlclassloader]# javac -d ./bin *.java
Note: TestUrlClassLoader.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
[root@docker-0 test-urlclassloader]# cd bin
[root@docker-0 bin]# ls
Test1.class  Test2.class  TestAnnotation.class
TestUrlClassLoader.class  TestUrlClassLoader$MyUrlClassLoader.class

創建jar文件

[root@docker-0 bin]# jar -cvf test.jar Test1.class Test2.class TestAnnotation.class
added manifest
adding: Test1.class(in = 963) (out= 570)(deflated 40%)
adding: Test2.class(in = 393) (out= 275)(deflated 30%)
adding: TestAnnotation.class(in = 453) (out= 266)(deflated 41%)
[root@docker-0 bin]# jar -tf test.jar
META-INF/
META-INF/MANIFEST.MF
Test1.class
Test2.class
TestAnnotation.class
[root@docker-0 bin]# rm -f Test1.class Test2.class TestAnnotation.class
[root@docker-0 bin]# ls
test.jar  TestUrlClassLoader.class  TestUrlClassLoader$MyUrlClassLoader.class

運行示例

[root@docker-0 bin]# java TestUrlClassLoader ./test.jar
MyURLClassLoader.loadClass:Test1
MyURLClassLoader.findClass:Test1
MyURLClassLoader.loadClass:java.lang.Object
java.lang.NoClassDefFoundError: TestAnnotation
	at TestUrlClassLoader.main(TestUrlClassLoader.java:21)
Caused by: java.lang.ClassNotFoundException: TestAnnotation
	at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	... 1 more
MyURLClassLoader.loadClass:TestAnnotation
MyURLClassLoader.findClass:TestAnnotation
MyURLClassLoader.loadClass:java.lang.annotation.Annotation
MyURLClassLoader.loadClass:java.lang.annotation.Target
MyURLClassLoader.loadClass:java.lang.annotation.Retention
MyURLClassLoader.loadClass:java.lang.annotation.RetentionPolicy
MyURLClassLoader.loadClass:java.lang.annotation.Documented
MyURLClassLoader.loadClass:java.lang.reflect.Proxy
MyURLClassLoader.loadClass:java.lang.Throwable
MyURLClassLoader.loadClass:java.lang.RuntimeException
MyURLClassLoader.loadClass:java.lang.Error
MyURLClassLoader.loadClass:java.lang.reflect.UndeclaredThrowableException
MyURLClassLoader.loadClass:java.lang.ClassNotFoundException
MyURLClassLoader.loadClass:java.lang.NoSuchMethodException
MyURLClassLoader.loadClass:java.lang.NoSuchMethodError
MyURLClassLoader.loadClass:java.lang.NoClassDefFoundError
MyURLClassLoader.loadClass:java.lang.reflect.InvocationHandler
MyURLClassLoader.loadClass:java.lang.Class
MyURLClassLoader.loadClass:java.lang.Integer
TestAnnotation.value:12

原文鏈接:https://blog.csdn.net/xuguofeng2016/article/details/125919434

欄目分類
最近更新