一次不引用包跑外部依赖的实践(五)再次尝试突破双亲委派加载主加载器线程池
1
app-tomcat
common
tool1(不可信任)
common
others1(不可信任)
tool2 -自定义加载器
common - 主加载器
others2 -自定义加载器
具体实施两种方案
1 构造无父加载器,与tomcat加载器平行,override findclass
tools2 - URLClassLoader.findClass()
common - this.getClass().getClassLoader().loadClass()
other2 - URLClassLoader.findClass()
2 构造以tomcat加载器为父加载器,override loadclass
tools2 - URLClassLoader.findClass()
common - this.getClass().getClassLoader().loadClass()
other2 - URLClassLoader.findClass()
侵入原理一样,根据经验,选1,安全,之所以仍然可以选1是因为他们是平行的,如果springboot就不行了
https://blog.csdn.net/weixin_48164819/article/details/126370786
1.继承ClassLoader类,重写findClass方法即可
2.若需要打破双亲委派机制则需要重写loadClass方法
4.判断两个类是否是同一个,除了看类名和全路径是否相同外,还需要看是否是同一个类加载器。因为相同包名和类名的类可以共存,只要加载器不一样。
/**
* 自定义类加载器:继承ClassLoader,重写findClass(默认是空实现)方法即可
* 加载自定义的路径下的类文件
*/
public class MyClassLoader extends ClassLoader {
//要加载的文件路径
private String classPath;
public MyClassLoader(String classPath) {
this.classPath = classPath;
}
/**
* 根据类名获取类路径并转换成字节数组
*/
private byte[] loadByte(String name) throws Exception {
name = name.replaceAll("\\.", "/");
FileInputStream fis = new FileInputStream(classPath + "/" + name + ".class");
int len = fis.available();
byte[] data = new byte[len];
fis.read(data);
fis.close();
return data;
}
/**
* 重写ClassLoader中的findClass方法加载类
*/
protected Class<?> findClass(String name) {
byte[] data = new byte[0];
try {
//根据类名获取字节数组
data = loadByte(name);
} catch (Exception e) {
e.printStackTrace();
}
//调用ClassLoader中加载类的方法
return defineClass(name, data, 0, data.length);
}
/**
* 打破双亲委派机制
* 重写loadClass方法
*/
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
Class<?> c = findLoadedClass(name);
if (c == null) {
long t1 = System.nanoTime();
//核心类包是不允许自己加载的,让父类加载
//否则会报java.lang.SecurityException:Prohibited package name:java.lang
//或D:\javaProject\javaLearn\JVMtest\java\lang\Object.class 系统找不到指定的路径
if(!name.startsWith("com.example.tuling_jvm.classload")){
c=this.getParent().loadClass(name);
}else{
//自己写的类,调用自定义加载类的方法
c = findClass(name);
}
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
public static void main(String[] args) throws Exception {
MyClassLoader myClassLoader = new MyClassLoader("D:/javaProject/javaLearn/JVMtest");
//调用自己写的loadClass方法
Class clazz = myClassLoader.loadClass("com.example.tuling_jvm.classload.Math1");
//已经加载的类,利用反射获取类,执行方法
Object obj = clazz.newInstance();
Method method = clazz.getDeclaredMethod("sout", null);
method.invoke(obj, null);
//双亲委派机制,若appClassLoader中有此类则加载此类,若没有则从自定义类中加载
System.out.println(clazz.getClassLoader().getClass().getName());
//模拟tomcat打破双亲委派机制,tomcat下不同war包不同版本可以相互隔离共存
// (每个war包有自己的WebAppClassLoader加载器,自己加载,不向上委托父类加载)
//仅仅只是文件路径不一致(模拟tomcat下的不同war包),类名,全路径都一致
MyClassLoader myClassLoader1 = new MyClassLoader("D:/javaProject/javaLearn/JVMtest1");
//调用自己写的loadClass方法
Class clazz1 = myClassLoader1.loadClass("com.example.tuling_jvm.classload.Math1");
//已经加载的类,利用反射获取类,执行方法
Object obj1 = clazz1.newInstance();
Method method1 = clazz1.getDeclaredMethod("sout", null);
method1.invoke(obj1, null);
//双亲委派机制,若appClassLoader中有此类则加载此类,若没有则从自定义类中加载
System.out.println(clazz1.getClassLoader().getClass().getName());
}
}
https://www.cnblogs.com/skiwdhwhssh/p/10340492.html
而Launcher$AppClassLoader的实现是没有遵循双亲委派模型的,它重的是loadClass方法
重写findClass方法是符合双亲委派模式的
2

抛错是55行

自己catch住打日志的是从49行forname来的

(上面是tomcat下面是subtool)



Log4jLoggerFactory类直接findClass,不让subFirst,否则会读到tomcat那边的类


tools2走的自定义加载器

日志走新包,线程池走parent

三方包如spark走新的
其他日志:

核心的common包走parent

核心tools2

核心三方依赖
其他:


Path是BootStrape或ExtClassLoader的,所以被双亲委派了
slf4j是tomcat加载器的,我们能加载自己的,日志显示在我们自己的ClassderPartalDependency里

这些包没有在子加载器找到,不过似乎不影响程序
浙公网安备 33010602011771号