简单例子
@Test
public void urlLoadTest() throws IOException {
System.out.println(System.getProperty("java.class.path"));
System.out.println(System.getProperty("java.ext.dirs"));
URLClassLoader loader = new URLClassLoader(new URL[] { new URL("file:D:\\workspaces\\") });
Enumeration<URL> result= loader.getResources("META-INF/spring.handlers");
List<URL> rResult=new ArrayList<>();
while (result.hasMoreElements()){
rResult.add(result.nextElement());
}
System.out.println(rResult);
}
执行结果
C:\Program Files\JetBrains\xxx\lib\idea_rt.jar;C:\Program Files\JetBrains\xxx\plugins\junit\lib\junit-rt.jar;C:\Program Files\JetBrains\xxx\plugins\junit\lib\junit5-rt.jar;C:\Program Files\Java\Java\jdk1.8.0_111\jre\lib\charsets.jar;C:\Program Files\Java\Java\jdk1.8.0_111\jre\lib\deploy.jar;C:\Program Files\Java\Java\jdk1.8.0_111\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\Java\jdk1.8.0_111\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\Java\jdk1.8.0_111\jre\lib\ext\dnsns.jar;C:\Program Files\Java\Java\jdk1.8.0_111\jre\lib\ext\jaccess.jar;C:\Program Files\Java\Java\jdk1.8.0_111\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\Java\jdk1.8.0_111\jre\lib\ext\localedata.jar;C:\Program Files\Java\Java\jdk1.8.0_111\jre\lib\ext\nashorn.jar;C:\Program Files\Java\Java\jdk1.8.0_111\jre\lib\ext\sunec.jar;C:\Program Files\Java\Java\jdk1.8.0_111\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\Java\jdk1.8.0_111\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\Java\jdk1.8.0_111\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\Java\jdk1.8.0_111\jre\lib\ext\zipfs.jar;C:\Program Files\Java\Java\jdk1.8.0_111\jre\lib\javaws.jar;C:\Program Files\Java\Java\jdk1.8.0_111\jre\lib\jce.jar;C:\Program Files\Java\Java\jdk1.8.0_111\jre\lib\jfr.jar;C:\Program Files\Java\Java\jdk1.8.0_111\jre\lib\jfxswt.jar;C:\Program Files\Java\Java\jdk1.8.0_111\jre\lib\jsse.jar;C:\Program Files\Java\Java\jdk1.8.0_111\jre\lib\management-agent.jar;C:\Program Files\Java\Java\jdk1.8.0_111\jre\lib\plugin.jar;C:\Program Files\Java\Java\jdk1.8.0_111\jre\lib\resources.jar;C:\Program Files\Java\Java\jdk1.8.0_111\jre\lib\rt.jar;D:\dev\project\private\project\quicklyCreateSpringMvc\miniSpring1\target\test-classes;D:\dev\project\private\project\quicklyCreateSpringMvc\miniSpring1\target\classes;C:\java环境\repository\maven\org\springframework\spring-webmvc\4.3.25.RELEASE\spring-webmvc-4.3.25.RELEASE.jar;C:\java环境\repository\maven\org\springframework\spring-aop\4.3.25.RELEASE\spring-aop-4.3.25.RELEASE.jar;C:\java环境\repository\maven\org\springframework\spring-beans\4.3.25.RELEASE\spring-beans-4.3.25.RELEASE.jar;C:\java环境\repository\maven\org\springframework\spring-context\4.3.25.RELEASE\spring-context-4.3.25.RELEASE.jar;C:\java环境\repository\maven\org\springframework\spring-core\4.3.25.RELEASE\spring-core-4.3.25.RELEASE.jar;C:\java环境\repository\maven\commons-logging\commons-logging\1.2\commons-logging-1.2.jar;C:\java环境\repository\maven\org\springframework\spring-expression\4.3.25.RELEASE\spring-expression-4.3.25.RELEASE.jar;C:\java环境\repository\maven\org\springframework\spring-web\4.3.25.RELEASE\spring-web-4.3.25.RELEASE.jar;C:\java环境\repository\maven\ch\qos\logback\logback-classic\1.2.3\logback-classic-1.2.3.jar;C:\java环境\repository\maven\ch\qos\logback\logback-core\1.2.3\logback-core-1.2.3.jar;C:\java环境\repository\maven\org\slf4j\slf4j-api\1.7.25\slf4j-api-1.7.25.jar;C:\java环境\repository\maven\junit\junit\4.12\junit-4.12.jar;C:\java环境\repository\maven\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar;C:\java环境\repository\maven\org\aspectj\aspectjrt\1.8.9\aspectjrt-1.8.9.jar;C:\java环境\repository\maven\org\aspectj\aspectjweaver\1.8.9\aspectjweaver-1.8.9.jar;C:\Program Files\JetBrains\xxx\lib\idea_rt.jar
C:\Program Files\Java\Java\jdk1.8.0_111\jre\lib\ext;C:\Windows\Sun\Java\lib\ext
[jar:file:/C:/java%e7%8e%af%e5%a2%83/repository/maven/org/springframework/spring-webmvc/4.3.25.RELEASE/spring-webmvc-4.3.25.RELEASE.jar!/META-INF/spring.handlers, jar:file:/C:/java%e7%8e%af%e5%a2%83/repository/maven/org/springframework/spring-aop/4.3.25.RELEASE/spring-aop-4.3.25.RELEASE.jar!/META-INF/spring.handlers, jar:file:/C:/java%e7%8e%af%e5%a2%83/repository/maven/org/springframework/spring-beans/4.3.25.RELEASE/spring-beans-4.3.25.RELEASE.jar!/META-INF/spring.handlers, jar:file:/C:/java%e7%8e%af%e5%a2%83/repository/maven/org/springframework/spring-context/4.3.25.RELEASE/spring-context-4.3.25.RELEASE.jar!/META-INF/spring.handlers]
其中loader层级
ExtClassLoader
APPClassLoader
URLClassLoader
以上层级是如何来的?我们可以看到最终是由Launcher 实现
public class URLClassLoader extends SecureClassLoader implements Closeable {
//指定classLoader
public URLClassLoader(URL[] urls, ClassLoader parent) {
super(parent);
....
}
//使用默认
public URLClassLoader(URL[] urls) {
super();
....
}
}
public class SecureClassLoader extends ClassLoader {
protected SecureClassLoader() {
super();
}
}
public abstract class ClassLoader {
protected ClassLoader() {
this(checkCreateClassLoader(), getSystemClassLoader());
}
public static ClassLoader getSystemClassLoader() {
initSystemClassLoader();
....
return scl;
}
//默认loader由Launcher产生
private static synchronized void initSystemClassLoader() {
sun.misc.Launcher l = sun.misc.Launcher.getLauncher();
....
scl = l.getClassLoader();
}
//最终赋值
private ClassLoader(Void unused, ClassLoader parent) {
this.parent = parent;
....
}
}
public class Launcher {
private static Launcher launcher = new Launcher();
//最终实现
public Launcher() {
Launcher.ExtClassLoader var1= Launcher.ExtClassLoader.getExtClassLoader();
this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
Thread.currentThread().setContextClassLoader(this.loader);
}
public static Launcher getLauncher() {
return launcher;
}
}
那么在getResources时ExtClassLoader、APPClassLoader、URLClassLoader三个loader怎么协作的?
public Enumeration<URL> getResources(String name) throws IOException {
@SuppressWarnings("unchecked")
Enumeration<URL>[] tmp = (Enumeration<URL>[]) new Enumeration<?>[2];
if (parent != null) {
tmp[0] = parent.getResources(name);
} else {
tmp[0] = getBootstrapResources(name);
}
tmp[1] = findResources(name);
return new CompoundEnumeration<>(tmp);
}
可以看到,其实是递归获取parent-loader的getResources的内容,最终合并到一起。
URLClassLoader loader = new URLClassLoader(new URL[] { new URL("file:D:\\workspaces\\") });
Enumeration<URL> result= loader.getResources("META-INF/spring.handlers");
所以,其实上面代码,等效于分别获取ExtClassLoader、APPClassLoader、URLClassLoader的findResources方法合并结果。
ExtClassLoader处理路径
static class ExtClassLoader extends URLClassLoader {
public static Launcher.ExtClassLoader getExtClassLoader() throws IOException {
final File[] var0 = getExtDirs();
...
}
private static File[] getExtDirs() {
String var0 = System.getProperty("java.ext.dirs");
File[] var1;
if (var0 != null) {
StringTokenizer var2 = new StringTokenizer(var0, File.pathSeparator);
..
} else {
var1 = new File[0];
}
return var1;
}
}
我们观察到两个点,
- 其实ExtClassLoader 继承自URLClassLoader (这是代码结构,而非parent,说明只是部分重写了URLClassLoader功能。)
- String var0 = System.getProperty("java.ext.dirs"),其实ExtClassLoader 就是对这个路径进行处理。
AppClassLoader处理路径
static class AppClassLoader extends URLClassLoader {
final URLClassPath ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this);
public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {
final String var1 = System.getProperty("java.class.path");
..
}
处理路径为System.getProperty("java.class.path"),即项目引用的各种lib里的jar包;
UrlClassLoader处理路径
通过参数外面传入
URLClassLoader loader = new URLClassLoader(new URL[] { new URL("file:D:\\workspaces\\") });
URLClassLoader.findResources方法
上面说到,getResources方法等效于等效于分别获取ExtClassLoader、APPClassLoader、URLClassLoader的findResources方法合并结果。
public class URLClassLoader extends SecureClassLoader implements Closeable {
public Enumeration<URL> findResources(final String name)
throws IOException
{
final Enumeration<URL> e = ucp.findResources(name, true);
...
}
}
public class URLClassPath {
public Enumeration<URL> findResources(final String var1, final boolean var2) {
return new Enumeration<URL>() {
private int index = 0;
private int[] cache = URLClassPath.this.getLookupCache(var1);
private URL url = null;
private boolean next() {
if (this.url != null) {
return true;
} else {
do {
URLClassPath.Loader var1x;
//获取相应loader,这一步可以理解为获取目录地址,例如java.class.path会有很多目录
if ((var1x = URLClassPath.this.getNextLoader(this.cache, this.index++)) == null) {
return false;
}
//获取相应拼接
this.url = var1x.findResource(var1, var2);
} while(this.url == null);
return true;
}
}
...
}
private synchronized URLClassPath.Loader getNextLoader(int[] var1, int var2) {
if (this.closed) {
return null;
} else if (var1 != null) {
...
} else {
return this.getLoader(var2);
}
}
private synchronized URLClassPath.Loader getLoader(int var1) {
if (this.closed) {
return null;
} else {
//循环完成以后才执行
while(this.loaders.size() < var1 + 1) {
}
//最终实现
return (URLClassPath.Loader)this.loaders.get(var1);
}
}
}
其中
var1x.findResource
三个实现了类
FileLoader:直接从文件流中获取
JarLoader:从jar中读取,基于zip压缩,拼接路径获取
Loader:。。