自定义类加载器 与 热加载
项目:MyMain:
package lc; /** * Created by sunyuming on 19/1/14. */ public class B {}
package lc;
/**
* Created by sunyuming on 19/1/14.
*/
public class A {
static {
System.out.println("父 A 加载");
}
public static void teststatic() {
;
}
public A() {
// 隐式加载B
B b = new B();
// 打印B的类加载器
System.out.println("B:--" + b.getClass().getClassLoader());
}
}
public class Main { public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, IllegalAccessException, InstantiationException { // System.out.println(System.getProperty("java.class.path")); // System.out.println(System.getProperty("user.dir")); String dir = "file:/Users/sunyuming/Documents/tool/jars//MySub-1.0.0-jar-with-dependencies.jar"; URL url = new URL(dir); URL[] urls2 = {url}; // 若不指定parent参数,则默认由系统类加载器担任自定义类加载器的父加载器,输出parent:sun.misc.Launcher$AppClassLoader@3764951d MyUrlClassLoader myUrlClassLoader = new MyUrlClassLoader(urls2); System.out.println("parent:--" + myUrlClassLoader.getParent()); // 由于A在父加载器(系统类加载器)classpath下,根据双亲委派,优先由父加载器加载,输出A:sun.misc.Launcher$AppClassLoader@3764951d Class CA = myUrlClassLoader.loadClass("lc.A"); System.out.println("A:--" + CA.getClassLoader()); CA.newInstance(); // 打破双亲委派机制,直接使用findclass绕开从父加载器寻找并由父加载器加载这一步,输出A:lc.Main$MyUrlClassLoader@5acf9800 // 当类加载请求到来时,先从缓存中查找该类对象,如果存在直接返回,如果不存在则交给该类加载去的父加载器去加载,倘若没有父加载则交给顶级启动类加载器去加载,最后倘若仍没有找到,则使用findClass()方法去加载 CA = myUrlClassLoader.findClass("lc.A"); System.out.println("A:--" + CA.getClassLoader()); // 实例化A,此时触发A的static代码块和B的加载,输出A:lc.Main$MyUrlClassLoader@5acf9800 // 由于B在父加载器classpath下,优先由系统类加载器加载,输出B:sun.misc.Launcher$AppClassLoader@3764951d CA.newInstance(); } private static class MyUrlClassLoader extends URLClassLoader { public MyUrlClassLoader(URL[] urls) { super(urls); }
/**
* 改写protected,使同包可访问findClass
* 注意,java的protected与c++不同,同包和子类都可访问
* @param name
* @return
* @throws ClassNotFoundException
*/
@Override protected Class<?> findClass(final String name) throws ClassNotFoundException { return super.findClass(name); } } }
项目:MySub
package lc; /** * Created by sunyuming on 19/1/14. */ public class B {}
package lc;
/**
* Created by sunyuming on 19/1/14.
*/
public class A {
static {
System.out.println("子 A 加载");
}
public static void teststatic() {
;
}
public A() {
// 隐式加载B
B b = new B();
// 打印B的类加载器
System.out.println("B:--" + b.getClass().getClassLoader());
}
}
mvn clean package
cp MySub/target/MySub-1.0.0-jar-with-dependencies.jar ~/Documents/tool/jars/
输出:
parent:--sun.misc.Launcher$AppClassLoader@49476842
A:--sun.misc.Launcher$AppClassLoader@49476842
父 A 加载
B:--sun.misc.Launcher$AppClassLoader@49476842
A:--lc.Main$MyUrlClassLoader@4517d9a3
子 A 加载
B:--sun.misc.Launcher$AppClassLoader@49476842
显式以自定义加载器加载~/Downloads/jars/中的jar包,模拟热加载jar包
实例化A
在A中以new的方式引用B,虚拟机隐式加载类B
依据双亲委派,首先委托AppClassLoader寻找classpath,找到后直接加载
若B也要实现热加载,必须:
myUrlClassLoader.findClass("lc.B");
因此:
虽然findclass能够在需要被热加载的jar包顶层入口类,完成热加载,但是其中的众多引用类已被AppClassLoader加载,若不一一指定自定义加载器并且使用findClass,则无法实现热加载,因此仍然建议将待热加载的jar包隔离在上层类加载器classpath之外,使之不可见,这样,其中的众多隐式加载也会被自定义类加载器所进行,从而实现热加载。
2019.12.19补充
快捷方式:指定线程的类加载器也许可以?
link:new操作时调用当前线程的类加载器,还是调用方的类加载器 / 两种类别的类加载器