網站首頁 編程語言 正文
本文將通過一個示例代碼說明URLClassLoader加載Class時的幾個問題:
- Class.forName(…)方法確實是使用傳遞的classLoader加載目標類
- initialize參數傳遞false不會初始化靜態資源
- 使用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
下面我們使用一個示例程序證明一下以下幾個問題:
- Class.forName(…)方法確實是使用傳遞的classLoader加載目標類
- initialize參數傳遞false不會初始化靜態資源
- 使用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
- 上一篇:實現自定義HTTP服務器
- 下一篇:mybatis源碼之集成springboot原理
相關推薦
- 2023-05-19 python?txt中的文件,逐行讀取并且每行賦值給變量問題_python
- 2022-11-09 Python運算符教程之邏輯門詳解_python
- 2023-11-17 python中numpy ndarray 按條件篩選數組,關聯篩選的例子——numpyarray對數
- 2022-07-18 linux系統安全和應用
- 2022-10-02 Redis常見限流算法原理及實現_Redis
- 2022-10-24 visual?studio?2022?編譯出來的文件被刪除并監視目錄中的文件變更(示例詳解)_C 語
- 2022-09-02 Python用matplotlib庫畫圖中文和負號顯示為方框的問題解決_python
- 2022-08-03 Python?權限控制模塊?Casbin_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支