1. 自定义 classloader
有时候,我们在项目开发的时候,会遇到比较恶心的问题,存在两个不同 jar 包,但是类的全限定名是一样的,而这两个包都不能删除,这时候调用可能就会出问题。
如何解决上面的问题? 我的答案就是自定义类加载器。
场景模拟
module-a
: 表示 a.jarmodule-b
: 表示 b.jarmodule-main
: 表示 程序入口
由于在 module-main
项目中同时引入了 module-a
和 module-b
这两个 jar 包,但是存在冲突类 HelloService
, 最终导致程序运行错误。
如何自定义 classloader,来解决问题?
- 重新定义
loadClass
方法,打破父类委托机制 - 使用
getResources
方法来获取所有的 class 文件,然后判断
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
| @SneakyThrows
public String test1(String message) {
// 查找类
ClassLoader classLoader = new ModuleAClassLoader();
Class<?> clazz = classLoader.loadClass("com.ooooo.HelloService");
// 执行
Method test1 = ReflectionUtils.findMethod(clazz, "test1", String.class);
Object result = test1.invoke(null, message);
return (String) result;
}
@SneakyThrows
public String test2(String message) {
// 查找类
ClassLoader classLoader = new ModuleBClassLoader();
Class<?> clazz = classLoader.loadClass("com.ooooo.HelloService");
// 执行
Method test2 = ReflectionUtils.findMethod(clazz, "test2", String.class);
Object result = test2.invoke(null, message);
return (String) result;
}
private static class ModuleAClassLoader extends ClassLoader {
public ModuleAClassLoader() {
super(ModuleAClassLoader.class.getClassLoader());
}
@SneakyThrows
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
Class<?> c = findLoadedClass(name);
if (c == null) {
// 当前路径下去找
if (name.contains("com.ooooo")) {
String path = name.replace(".", "/") + ".class";
Enumeration<URL> resources = getResources(path);
URL targetUrl = null;
while (resources.hasMoreElements()) {
targetUrl = resources.nextElement();
if (targetUrl.toString().contains("module-a")) {
break;
}
}
// 读取 class 文件
InputStream in = targetUrl.openStream();
byte[] bytes = StreamUtils.copyToByteArray(in);
in.close();
c = defineClass(name, bytes, 0, bytes.length);
}
}
if (c == null) {
c = getParent().loadClass(name);
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
private static class ModuleBClassLoader extends ClassLoader {
public ModuleBClassLoader() {
super(ModuleAClassLoader.class.getClassLoader());
}
@SneakyThrows
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
Class<?> c = findLoadedClass(name);
if (c == null) {
// 当前路径下去找
if (name.contains("com.ooooo")) {
String path = name.replace(".", "/") + ".class";
Enumeration<URL> resources = getResources(path);
URL targetUrl = null;
while (resources.hasMoreElements()) {
targetUrl = resources.nextElement();
if (targetUrl.toString().contains("module-b")) {
break;
}
}
// 读取 class 文件
InputStream in = targetUrl.openStream();
byte[] bytes = StreamUtils.copyToByteArray(in);
in.close();
c = defineClass(name, bytes, 0, bytes.length);
}
}
if (c == null) {
c = getParent().loadClass(name);
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
}
|
2. 代码实现位置
github 地址