Tomcat打破双亲委派机制实现各应用程序的类库相互隔离原理与实现demo

1、实现原理


以Tomcat类加载为例,Tomcat 如果使用默认的双亲委派类加载机制行不行?
我们思考一下:Tomcat是个web容器, 那么它要解决什么问题:
1. 一个web容器可能需要部署两个应用程序,不同的应用程序可能会依赖同一个第三方类库的
不同版本,不能要求同一个类库在同一个服务器只有一份,因此要保证每个应用程序的类库都是
独立的,保证相互隔离。
2. 部署在同一个web容器中相同的类库相同的版本可以共享。否则,如果服务器有10个应用程
序,那么要有10份相同的类库加载进虚拟机。
3. web容器也有自己依赖的类库,不能与应用程序的类库混淆。基于安全考虑,应该让容器的
类库和程序的类库隔离开来。
4. web容器要支持jsp的修改,我们知道,jsp 文件最终也是要编译成class文件才能在虚拟机中
运行,但程序运行后修改jsp已经是司空见惯的事情, web容器需要支持 jsp 修改后不用重启。


2、tomcat的几个主要类加载器


commonLoader:Tomcat最基本的类加载器,加载路径中的class可以被Tomcat容
器本身以及各个Webapp访问;
catalinaLoader:Tomcat容器私有的类加载器,加载路径中的class对于Webapp不
可见;
sharedLoader:各个Webapp共享的类加载器,加载路径中的class对于所有
Webapp可见,但是对于Tomcat容器不可见;
WebappClassLoader:各个Webapp私有的类加载器,加载路径中的class只对当前
Webapp可见,比如加载war包里相关的类,每个war包应用都有自己的
WebappClassLoader,实现相互隔离,比如不同war包应用引入了不同的spring版本,
这样实现就能加载各自的spring版本;

注意:
同一个JVM内,两个相同包名和类名的类对象可以共存,因为他们的类加载器可以不一
样,所以看两个类对象是否是同一个,除了看类的包名和类名是否都相同之外,还需要他们的类
加载器也是同一个才能认为他们是同一个。
 

3、JavaDemo

/**
 * @author WuSong
 * @version 1.0
 * @date 2022/12/7 16:59
 * @description
 */
public class MyTomcatClassLoader {

    static class TomcatClassLoader extends ClassLoader {
        private String classPath;

        public TomcatClassLoader(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;
        }

        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            try {

                byte[] data = loadByte(name);
                return defineClass(name, data, 0, data.length);
            } catch (Exception e) {
                e.printStackTrace();
                throw new ClassNotFoundException();
            }
        }

        /**
         * 重写类加载方法,实现自己的加载逻辑,不委派给双亲加载
         * @param name
         * @param resolve
         * @return
         * @throws ClassNotFoundException
         */
        @Override
        protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
            synchronized (getClassLoadingLock(name)){
                // 首先,检查类是否已加载
                Class<?> c = findLoadedClass(name);

                if(c == null){
                    // 如果仍然找不到,则按顺序调用findClass
                    // to find the class.
                    long t0 = System.nanoTime();
                    long t1 = System.nanoTime();

                    // TODO 核心改动点:非自定义的类还是走双亲委派加载
                    if(!name.startsWith("com.wusong.jvm")){
                        c = this.getParent().loadClass(name);
                    }else{
                        c = findClass(name);
                    }

                    // 定义类加载器;记录统计数据
                    PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    PerfCounter.getFindClasses().increment();
                }

                if (resolve) {
                    resolveClass(c);
                }
                return c;
            }
        }


    }


    public static void main(String[] args) throws Exception {

        TomcatClassLoader classLoader = new TomcatClassLoader("D:/test");
        // java文件的本地目录:D:/test/com/wusong/jvm/User.java
        Class clazz = classLoader.loadClass("com.wusong.jvm.User");
        Object obj = (Object) clazz.newInstance();
        Method method = clazz.getDeclaredMethod("sout", null);
        method.invoke(obj, null);
        System.out.println(clazz.getClassLoader().getClass().getName());

        TomcatClassLoader classLoader2 = new TomcatClassLoader("D:/test1");
        // java文件的本地目录:D:/test/com/wusong/jvm/User.java
        Class clazz2 = classLoader2.loadClass("com.wusong.jvm.User");
        Object obj2 = (Object) clazz2.newInstance();
        Method method2 = clazz2.getDeclaredMethod("sout", null);
        method2.invoke(obj2, null);
        System.out.println(clazz2.getClassLoader().getClass().getName());

    }

}

4、运行结果

可以看到最终的运行结果,类路径是同一个:com.wusong.jvm.User,但是读取出来是不同的类,这样就实现了相互隔离的效果

5、总结

tomcat就是利用了打破双亲委派机制,让运行在tomcat里面的war包、jar包的相同类目不同版本的类的相互隔离,和相同版本相同类的共享

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱敲代码的小松

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值