Resource

引子

事件描述

​ 开发环境:Mac, dea,Spring boot 1.5.3.RELEASE

​ 在开发微信小程序客服消息的时候,需要针对context为1的内容回复一张图片。

​ 因为,微信小程序相关的配置都是写在Common模板下的,所以就把图片放在Common模块下的resource中。

image-20190522164029948

​ IDEA中,使用以下代码都能拿到图片对象。

1
InputStream is = ClassLoader.getSystemResourceAsStream("qrcode_for_gh_ac1de8498046_258.jpg");
1
InputStream is = WxMaServiceConfiguration.class.getResourceAsStream("/qrcode_for_gh_ac1de8498046_258.jpg");

​ 当项目部署到服务器上的tomcat的时候,以上两个方法就都找不到图片对象了。

1
2
3
4
5
6
7
-webroot
-WEB-INF
-classes
-application.properties
......
-lib
-xxx-common-1.0.0-SNAPSHOT.jar(jarz中包含了图片)

​ 为什么开发环境可以加载,部署到服务器上就加载不了了呢?

分析

ClassLoader.getSystemResourceAsStream

java.lang.ClassLoader

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
public abstract class ClassLoader {

public static InputStream getSystemResourceAsStream(String name) {
URL url = getSystemResource(name);
try {
return url != null ? url.openStream() : null;
} catch (IOException e) {
return null;
}
}

public static URL getSystemResource(String name) {
ClassLoader system = getSystemClassLoader();
if (system == null) {
return getBootstrapResource(name);
}
return system.getResource(name);
}

@CallerSensitive
public static ClassLoader getSystemClassLoader() {
initSystemClassLoader();
if (scl == null) {
return null;
}
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkClassLoaderPermission(scl, Reflection.getCallerClass());
}
return scl;
}

private static synchronized void initSystemClassLoader() {
if (!sclSet) {
if (scl != null)
throw new IllegalStateException("recursive invocation");
sun.misc.Launcher l = sun.misc.Launcher.getLauncher();
if (l != null) {
Throwable oops = null;
scl = l.getClassLoader();
try {
scl = AccessController.doPrivileged(
new SystemClassLoaderAction(scl));
} catch (PrivilegedActionException pae) {
oops = pae.getCause();
if (oops instanceof InvocationTargetException) {
oops = oops.getCause();
}
}
if (oops != null) {
if (oops instanceof Error) {
throw (Error) oops;
} else {
// wrap the exception
throw new Error(oops);
}
}
}
sclSet = true;
}
}
private static URL getBootstrapResource(String name) {
URLClassPath ucp = getBootstrapClassPath();
Resource res = ucp.getResource(name);
return res != null ? res.getURL() : null;
}

static URLClassPath getBootstrapClassPath() {
// Bootstrap 从 Launcher 的方法中初始化
return sun.misc.Launcher.getBootstrapClassPath();
}
......
}

sun.misc.Launcher

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

public class Launcher {
private static URLStreamHandlerFactory factory = new Launcher.Factory();
private static Launcher launcher = new Launcher();
// {$JAVA_HOME}/jre/lib 和 {$JAVA_HOME}/jre/classes,Bootstrap ClassLoader的路径
private static String bootClassPath = System.getProperty("sun.boot.class.path");
private ClassLoader loader;

......

public Launcher() {
Launcher.ExtClassLoader var1;
try {
// ExtClassLoader
var1 = Launcher.ExtClassLoader.getExtClassLoader();
} catch (IOException var10) {
throw new InternalError("Could not create extension class loader", var10);
}

try {
// AppClassLoader
this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
} catch (IOException var9) {
throw new InternalError("Could not create application class loader", var9);
}

Thread.currentThread().setContextClassLoader(this.loader);
......

}

public static URLClassPath getBootstrapClassPath() {
return Launcher.BootClassPathHolder.bcp;
}

......

private static class BootClassPathHolder {
// URLClassPath
static final URLClassPath bcp;
private BootClassPathHolder() {}

static {
URL[] var0;
if (Launcher.bootClassPath != null) {
var0 = (URL[])AccessController.doPrivileged(new PrivilegedAction<URL[]>() {
public URL[] run() {
File[] var1 = Launcher.getClassPath(Launcher.bootClassPath);
int var2 = var1.length;
HashSet var3 = new HashSet();

for(int var4 = 0; var4 < var2; ++var4) {
File var5 = var1[var4];
if (!var5.isDirectory()) {
var5 = var5.getParentFile();
}

if (var5 != null && var3.add(var5)) {
MetaIndex.registerDirectory(var5);
}
}

return Launcher.pathToURLs(var1);
}
});
} else {
var0 = new URL[0];
}

bcp = new URLClassPath(var0, Launcher.factory, (AccessControlContext)null);
bcp.initLookupCache((ClassLoader)null);
}
}

static class AppClassLoader extends URLClassLoader {
public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {
final String var1 = System.getProperty("java.class.path");
final File[] var2 = var1 == null ? new File[0] : Launcher.getClassPath(var1);
return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<Launcher.AppClassLoader>() {
public Launcher.AppClassLoader run() {
URL[] var1x = var1 == null ? new URL[0] : Launcher.pathToURLs(var2);
return new Launcher.AppClassLoader(var1x, var0);
}
});
}
......
}
}


从上面代码可以知道,java.lang.ClassLoader#getSystemClassLoader 获取是 sun.misc.Launcher.AppClassLoader

java.lang.ClassLoader#getBootstrapResource 方法,返回的是 sun.misc.URLClassPath 对象。这个对象是在rt.jar包中的。

https://blog.csdn.net/briblue/article/details/54973413 详细看下 这里的classloader内容。

XXX.class.getResourceAsStream

WebappClassLoader 可以加载 jar中的资源文件

WebappClassLoader
context:
delegate: false
repositories:
/WEB-INF/classes/
———-> Parent Classloader:
org.apache.catalina.loader.StandardClassLoader@2145b572

1
2
3
ClassLoader.getSystemClassLoader().getClass()

class sun.misc.Launcher$AppClassLoader