• 王之刚
    2019-07-06
    最后的问题没有想明白,有人能详细解释一下吗?

    作者回复: 线程上下文加载器其实是线程的一个私有数据,跟线程绑定的,这个线程做完启动Context组件的事情后,会被回收到线程池,之后被用来做其他事情,为了不影响其他事情,需要恢复之前的线程上下文加载器。

     1
     13
  • Cy190622
    2019-07-06

    老师好,您讲个很通透。还有一点问题请教一下:
    1.线程上下文的加载器是不是指定子类加载器来加载具体的某个桥接类。比如JDBC的Driver的加载。
    2.每个Web下面的java类和jar(WEB-INF/classes和WEB-INF/lib),都是WebAppClassLoader加载吗?
    3.Web容器指定的共享目录一般是在什么路径下

    作者回复: 1和2你说的都准确。

    CommonClassLoader对应<Tomcat>/common/*
    CatalinaClassLoader对应 <Tomcat >/server/*
    SharedClassLoader对应 <Tomcat >/shared/*
    WebAppClassloader对应 <Tomcat >/webapps/<app>/WEB-INF/*目录

     1
     13
  • nightmare
    2019-07-06
    老师,上下文加载器是不是比如说我在加载spring的线程设置为webappclassloader那么就算spring的jar是由shared classloader加载的,那么spring加载的过程中也是由webappclassloader来加载,而用完设置回去,是因为我只需要跨classloader的时候才需要线程上下文加载器

    作者回复: 是的👍

    
     7
  • 每天一点点
    2019-08-01
    课后思考题
    先切换 WebAppClassLoader 是因为 tomcat 的加载机制,需要先加载 web 的类,然后在共享类等
    老师,对么?

    作者回复: 对的

    
     2
  • Li Shunduo
    2019-07-10
    Tomcat 9中只有这些目录了: conf,    logs,    bin, lib, temp, work, webapps.
    并没有下面这些类加载器对应的common/shared/server目录,是需要自己手工创建吗?
    CommonClassLoader对应<Tomcat>/common/*
    CatalinaClassLoader对应 <Tomcat >/server/*
    SharedClassLoader对应 <Tomcat >/shared/*
    WebAppClassloader对应 <Tomcat >/webapps/<app>/WEB-INF/*目录
    展开

    作者回复: 你可以在Tomcat conf目录下的Catalina.properties文件里配置各种类加载器的加载路径

    
     1
  • 清风
    2019-07-09
    看代码,CommonClassLoader,CatalinaClassLoader,SharedClassLoader引用了同一个对象,这样的话,是怎么做到类空间隔离的呢

    作者回复: 它们是不同的类加载器实例,实例,实例,不同实例的类加载器加载的同名类是不同的

    
     1
  • nightmare
    2019-07-06
    老师我今天做了试验,在tomcat下和conf同级建立shared目录,然后把两个项目的spring的jar包放到shared目录下,然后webapp/class下的spring的jar包删除,启动报找不到spring的jar包,tomcat版本为7.x,是不是还需要配置什么啊,请老师帮忙指导一下

    作者回复: 你可以在Tomcat conf目录下的catalina.properties文件中配置各加载器的加载路径

     1
     1
  • 业余爱好者
    2019-07-06
    之前做了一个项目,在tomcat下面部署了两个springboot打的war,这两个war都依赖了同一个数据访问用的jar包,tomcat在启动第二个war项目时,报什么datasource已经实例化的一个错误,导致第二个项目启动失败。后来查了下资料,在application.yml里禁用了jmx解决。

    虽然问题解决了,但却不明就里,不知道是不是web应用没有做隔离的缘故。不知道这样理解对不对。。

    作者回复: 应该在Tomcat安装目录下建一个shared目录,把web应用共享的库放这个目录下

     1
     1
  • 大漠落木
    2019-07-06
    找不到 CommonClassLoader CatalinaClassLoader SharedClassLoader 这三个类

    public class WebappClassLoader extends WebappClassLoaderBase

    public abstract class WebappClassLoaderBase extends URLClassLoader

    作者回复: 前面三个是加载器实例名,不是类名,你可以在BootStrap.java中找到

    
     1
  • Demon.Lee
    2020-01-08
    老师,请教下,我把spring-boot相关的jar都放在 <Tomcat >/shared/下面,个别spring包(比如spring-boot-starter-data-rest-1.4.3.RELEASE.jar,spring-data-rest-core-2.5.6.RELEASE.jar,spring-data-rest-webmvc-2.5.6.RELEASE.jar等3个jar包)还在<Tomcat >/webapps/<app>/WEB-INF/lib/下,因为只有个别app才用得到这几个jar,但是现在启动报错,Caused by: java.lang.NoClassDefFoundError: org/springframework/data/rest/webmvc/config/RepositoryRestConfigurerAdapter;所以,我就没完全理解类加载的顺序了。请老师答复下。
    
    
  • Demon.Lee
    2019-12-27
    老师,请教下,我用的spring-boot,把相关spring的包都放在了xx/tomcat8.5.50/shared下面,其中有2个是redis相关的jar包(jedis-2.9.0.jar,spring-data-redis-1.8.7.RELEASE.jar), 业务代码存数据到redis时是setObject(String key, Object value),其中value是一个业务对象实例如Student(这个类在webapp/WEB-INF/classes下面), 运行没有问题,但是取数据时,解析报错,Student对象找不到:
    org.springframework.data.redis.serializer.SerializationException: Cannot deserialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to deserialize payload. Is the byte array a result of corresponding serialization for DefaultDeserializer?; nested exception is org.springframework.core.NestedIOException: Failed to deserialize object type; nested exception is java.lang.ClassNotFoundException: com.xxx.xxx.domain.Student
        at org.springframework.data.redis.serializer.JdkSerializationRedisSerializer.deserialize(JdkSerializationRedisSerializer.java:82)
        at org.springframework.data.redis.core.AbstractOperations.deserializeValue(AbstractOperations.java:318)
        at org.springframework.data.redis.core.AbstractOperations$ValueDeserializingRedisCallback.doInRedis(AbstractOperations.java:58)
        at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:207)
        at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:169)
        at org.springframework.data.redis.core.AbstractOperations.execute(AbstractOperations.java:91)
        at org.springframework.data.redis.core.DefaultValueOperations.get(DefaultValueOperations.java:43)

    所以我理解是SharedClassLoader无法加载webAppClassLoader加载的类,那该如何解决这个问题呢,因为业务代码暂时不能改。
    展开
    
    
  • z.l
    2019-11-23
    老师,想到个问题,tomcat加载自身类的时候为什么不能用一个单独的WebAppClassLoader实例来加载,这样不仅不会和web应用的类加载冲突,而且还能把CatalinaClassLoader、CommonClassLoader这俩加载器都省了,简单明了。
    
    
  • 帽子丨影
    2019-09-25
    老师好,有个疑问。既然不同的类加载器实例加载的类是不同的,那如果Tomcat给每一个context使用各自的AppClassLoader实例来加载,那不是也可以达到应用隔离的目标了吗

    作者回复: Tomcat正是这样做的

    
    
  • 无心水
    2019-09-24
    蓝色部分就是JVM的类加载器分层结构。Tomcat在JVM的基础上又加了三层。线程上下文类加载器,打破了双亲委托机制。
    
    
  • Demter
    2019-08-22
    老师,spring的依赖类具体是指哪些啊

    作者回复: 就是你在使用Spring时需要导入的那一堆jar

    
    
  • gogo
    2019-08-21
    老师 我每次看完一篇之后 都很有收获 觉得好兴奋 可能是因为我太激动了 就有点不求甚解了 说真的 很希望老师再出一个专栏 老师再出一个专栏 必定大卖!
    
    
  • 疯狂咸鱼
    2019-08-16
    老师,JVM判断两个class实例相不相同,一个是看类加载器,另一个是看全路径名?那么如果全限定名不一样的servlet,为什么不能同时被一个加载器加载?

    作者回复: 两个不同的servlet可以被一个加载器加载

    
    
  • 吕
    2019-07-17
    今天把springboot打成的多个war包放到tomcat,并且设置了host,但是无法访问到静态资源,而放到Root下就可以,实在搞不懂了

    作者回复: 这好像是Tomcat在代码里写死的逻辑

     1
    
  • 一颗苹果
    2019-07-07
    老师请问下,如果tomcat的不同应用引用了不同版本的spring依赖,sharedClassloader 怎么区分不同版本呢

    作者回复: 这种情况就不是公共类库了,应该放到各Web应用的路径下去

    
    
  • 玉芟
    2019-07-07
    老师,您好:
    我对Thread.currentThread().setContextClassLoader(ClassLoader cl)用法一直有个疑问:
    - setContextClassLoader以后是不是只能显示地通过getContextClassLoader获取ClassLoader后调用loadClass(String name, boolean resolve)方法加载类才能是自定义加载器加载的(验证方法:打印obj.getClass().getClassLoader())?
    - setContextClassLoader以后通过Class.forName(String name)方法等反射得到的类是不是就只能是AppClassLoader加载的?
    我做了个实验:
    自定义类加载器:
    public class DIYClassLoader extends URLClassLoader {
        public DIYClassLoader(URL[] urls) { super(urls); }
        /**
         * 策略很简单:
         * 1)、首先尝试ExtClassLoader|BootstrapClassLoader加载
         * 2)、之后尝试自己加载
         * 3)、最后尝试真正父加载器加载
         */
        @Override
        protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
            Class<?> c = findLoadedClass(name);
            ClassLoader parent = getParent();
            if (parent != null) {
                ClassLoader ecl = parent;
                while (ecl.getParent() != null)// 找ExtClassLoader
                    ecl = ecl.getParent();
                try {
                    c = ecl.loadClass(name);
                } catch (ClassNotFoundException e) { }
                if (c == null) {
                    try {
                        c = findClass(name);// DIYClassLoader自己来
                    } catch (ClassNotFoundException e) {}
                    if (c == null) {
                        // 尝试真正父加载器加载,多半是AppClassLoader
                        c = parent.loadClass(name);
                    }
                }
            }else {
                // 直接自己尝试加载
                c = findClass(name);
            }
            if (resolve)
                resolveClass(c);
            return c;
        }
    }
    main方法:
    URL url = Main.class.getClassLoader().getResource(".");
    DIYClassLoader scl = new DIYClassLoader(new URL[] {url});
    Thread.currentThread().setContextClassLoader(scl);
    Class clazz = Class.forName("xx.xx.Xxx");
    // sun.misc.Launcher$AppClassLoader@18b4aac2
    clazz = scl.loadClass("xx.xx.Xxx");
    // xx.xx.DIYClassLoader@682a0b20
    不知道我把问题描述清楚了吗?还望老师解答
    展开

    作者回复: 线程上下文加载器本质是线程私有数据,需要显式拿出来,调getContextClassLoader拿

    
    
我们在线,来聊聊吧