一次不引用包跑外部依赖的实践(五)再次尝试突破双亲委派加载主加载器线程池

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方法是符合双亲委派模式的

 

 

 抛错是55行

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

 

(上面是tomcat下面是subtool)

 

 

 

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

 

tools2走的自定义加载器

 日志走新包,线程池走parent

 

 

三方包如spark走新的

 

 

其他日志:

 核心的common包走parent

 核心tools2

 核心三方依赖

 

 

其他:

Path是BootStrape或ExtClassLoader的,所以被双亲委派了

slf4j是tomcat加载器的,我们能加载自己的,日志显示在我们自己的ClassderPartalDependency里

 

 

这些包没有在子加载器找到,不过似乎不影响程序

posted on 2025-03-21 20:29  silyvin  阅读(19)  评论(0)    收藏  举报