-
Notifications
You must be signed in to change notification settings - Fork 1.5k
/
RoutingURLClassLoader.java
155 lines (135 loc) · 5.57 KB
/
RoutingURLClassLoader.java
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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
package com.alibaba.jvm.sandbox.core.classloader;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Enumeration;
import com.alibaba.jvm.sandbox.core.classloader.BusinessClassLoaderHolder.DelegateBizClassLoader;
/**
* 可路由的URLClassLoader
*
* @author luanjia@taobao.com
*/
public class RoutingURLClassLoader extends URLClassLoader {
private static final Logger logger = LoggerFactory.getLogger(RoutingURLClassLoader.class);
private final ClassLoadingLock classLoadingLock = new ClassLoadingLock();
private final Routing[] routingArray;
public RoutingURLClassLoader(final URL[] urls,
final Routing... routingArray) {
super(urls);
this.routingArray = routingArray;
}
public RoutingURLClassLoader(final URL[] urls,
final ClassLoader parent,
final Routing... routingArray) {
super(urls, parent);
this.routingArray = routingArray;
}
@Override
public URL getResource(String name) {
URL url = findResource(name);
if (null != url) {
return url;
}
url = super.getResource(name);
return url;
}
@Override
public Enumeration<URL> getResources(String name) throws IOException {
Enumeration<URL> urls = findResources(name);
if (null != urls) {
return urls;
}
urls = super.getResources(name);
return urls;
}
@Override
protected Class<?> loadClass(final String javaClassName, final boolean resolve) throws ClassNotFoundException {
return classLoadingLock.loadingInLock(javaClassName, new ClassLoadingLock.ClassLoading() {
@Override
public Class<?> loadClass(String javaClassName) throws ClassNotFoundException {
// 优先查询类加载路由表,如果命中路由规则,则优先从路由表中的ClassLoader完成类加载
if (ArrayUtils.isNotEmpty(routingArray)) {
for (final Routing routing : routingArray) {
if (!routing.isHit(javaClassName)) {
continue;
}
final ClassLoader routingClassLoader = routing.classLoader;
try {
return routingClassLoader.loadClass(javaClassName);
} catch (Exception cause) {
// 如果在当前routingClassLoader中找不到应该优先加载的类(应该不可能,但不排除有就是故意命名成同名类)
// 此时应该忽略异常,继续往下加载
// ignore...
}
}
}
// 先走一次已加载类的缓存,如果没有命中,则继续往下加载
final Class<?> loadedClass = findLoadedClass(javaClassName);
if (loadedClass != null) {
return loadedClass;
}
try {
Class<?> aClass = findClass(javaClassName);
if (resolve) {
resolveClass(aClass);
}
return aClass;
} catch (Exception cause) {
DelegateBizClassLoader delegateBizClassLoader = BusinessClassLoaderHolder.getBussinessClassLoader();
try {
if(null != delegateBizClassLoader){
return delegateBizClassLoader.loadClass(javaClassName,resolve);
}
} catch (Exception e) {
//忽略异常,继续往下加载
}
return RoutingURLClassLoader.super.loadClass(javaClassName, resolve);
}
}
});
}
/**
* 类加载路由匹配器
*/
public static class Routing {
private final Collection<String/*REGEX*/> regexExpresses = new ArrayList<String>();
private final ClassLoader classLoader;
/**
* 构造类加载路由匹配器
*
* @param classLoader 目标ClassLoader
* @param regexExpressArray 匹配规则表达式数组
*/
Routing(final ClassLoader classLoader, final String... regexExpressArray) {
if (ArrayUtils.isNotEmpty(regexExpressArray)) {
regexExpresses.addAll(Arrays.asList(regexExpressArray));
}
this.classLoader = classLoader;
}
/**
* 当前参与匹配的Java类名是否命中路由匹配规则
* 命中匹配规则的类加载,将会从此ClassLoader中完成对应的加载行为
*
* @param javaClassName 参与匹配的Java类名
* @return true:命中;false:不命中;
*/
private boolean isHit(final String javaClassName) {
for (final String regexExpress : regexExpresses) {
try {
if (javaClassName.matches(regexExpress)) {
return true;
}
} catch (Throwable cause) {
logger.warn("routing {} failed, regex-express={}.", javaClassName, regexExpress, cause);
}
}
return false;
}
}
}