美文网首页
Tomcat类加载机制之隔离

Tomcat类加载机制之隔离

作者: overflow_e4e4 | 来源:发表于2019-04-29 14:42 被阅读0次

    Tomcat类加载机制之隔离

    对于很多非框架的java开发者来说,classloader确实很少用到。我最近看了两篇优秀博文深入浅出classLoader深入理解Tomcat(五)类加载机制,给了我很大的启发以及思考。

    • 为什么classLoader在一些场合是必须的?

    对于一般的开发者来说main函数即可运行一个完整的java程序(其实启动一个java进程也是用到了java自带的classLoader)。但考虑到tomcat这种容器类的程序,上面两篇博文中讲到

    一个Tomcat容器允许同时运行多个Web程序,每个Web程序依赖的类又必须是相互隔离的

    假如我们有两个Web程序,一个依赖A库的1.0版本,另一个依赖A库的2.0版本,他们都使用了类xxx.xx.Clazz,其实现的逻辑因类库版本的不同而结构完全不同。那么这两个Web程序的其中一个必然因为加载的Clazz不是所使用的Clazz而出现问题!而这对于开发来说是非常致命的!

    看到这个我才有一种恍然大悟的感觉,原来我从来没考虑过这样的需求和环境,所以说框架的开发者们真的考虑了很多。膜拜!

    • 那么Tomcat是如何做到的呢?

    Tomcat的 类加载顺序(开启了delegate模式)

    delegate模式

    在Tomcat中,默认的行为是先尝试在Bootstrap和Extension中进行类型加载,如果加载不到则在WebappClassLoader中进行加载,如果还是找不到则在Common中进行查找。在Alibaba使用的Tomcat开启了delegate模式,因此加载类型时会以parent类加载器优先。

    关于这点官网也有文档说明

    我的理解是只要用两个不同的类加载器,加载同一个类即可实现相互隔离,但是要注意双亲委派模型,不要让父类加载器加载到你要加载的类。

    • 我按照这一原理简单实现的demo

    这个类有两个classloader加载两次classloader.Container类

    package classloader;
    import java.net.URL;
    import java.net.URLClassLoader;
    
    /**
     * 这几天在看了 tomcat的 类加载器 产生了1个疑问
     * 如果一个tomcat内有了两个webApp, 那么启动的jvm进程应该1个
     * 那如果保证webApp1 和webApp2之间内存数据互不影响?
     */
     
    public class ClassLoaderDemo {
        public static void main(String... args) throws Exception {
            URL url = new URL("file:E:\\testContainer.jar");
            URLClassLoader classloader1 = new URLClassLoader(new URL[]{url});
            URLClassLoader classloader2 = new URLClassLoader(new URL[]{url});
            Class clazz1 = Class.forName("classloader.Container", true, classloader1);
            Class clazz2 = Class.forName("classloader.Container", true, classloader2);
            System.out.println(clazz1);
            System.out.println(clazz2);
            System.out.println(clazz1==clazz2);
            System.out.println(clazz1.equals(clazz2));
            clazz1.getMethod("addEntry").invoke(clazz1.newInstance());
            clazz1.getMethod("printEntries").invoke(clazz1.newInstance());
            clazz2.getMethod("printEntries").invoke(clazz2.newInstance());
        }
    }
    

    这个类是一个容器类,把他打包在testContainer.jar中供上一个类调用

    package classloader;
    
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * 虚拟一个容器 假设是一个 webApp容器,注意这个类不要让上述的main类的类加载器加载到。
     * 最简单的方式就是把他们放在不同的项目中,并把这个类打成jar包 然后让URLClassLoader去加载jar包中的这个类
     *
     * @author xuecm
     */
    public class Container {
    
        public static Map<String, String> map = new HashMap<>();
    
        public void addEntry() {
            map.put("key", "value");
        }
    
        public void printEntries() {
            System.out.println("start printEntries ===========");
    
            for (Map.Entry<String, String> entry : map.entrySet()) {
                System.out.println(entry.getKey() + "_" + entry.getValue());
            }
            System.out.println("end printEntries ============");
    
        }
    }
    

    最后的输出结果

    class classloader.Container
    class classloader.Container
    false
    false
    start printEntries ===========
    key_value
    end printEntries ============
    start printEntries ===========
    end printEntries ============
    
    

    可以看到输出的结果中两个classLoader加载的classloader.Container 并不是同一个,而且对一个Container的addEntry操作并没有影响另外一个。
    以上就实现了简单容器的封装。

    相关文章

      网友评论

          本文标题:Tomcat类加载机制之隔离

          本文链接:https://www.haomeiwen.com/subject/skpynqtx.html