Web 容器 Jetty 多 ClassLoader 重复加载 Class 导致 java.lang.LinkageError

来源:互联网 发布:java之父 编辑:程序博客网 时间:2024/05/18 00:25


  • 问题描述:
    最近团队在使用 Axis2 开发 WebService 服务时,遇到服务发布成功,但调用一直出现如下错误


    Axis2 Can’t find Spring’s ApplicationContext

  • 问题分析:
    查看异常出处:SpringAppContextAwareObjectSupplier.getServiceObject 方法可知
    ApplicationContext 由 ApplicationContextHolder.getContext() 调用得到,再看
    ApplicationContextHolder 类可知其实现了 ApplicationContextAware 接口只是负责管理注入的 Spring 上下文对象置于静态变量 appCtx 供 Axis2 调用

  • 提出一种可行方案 A:
    直接在 Spring 配置文件中注入 ApplicationContext 至 ApplicationContextHolder.appCtx

  • A 方案效果:
    Axis2 WebService 服务可正常发布,但调用依然是出现同样的错误

  • 对 A 方案结果提出疑问:
    难道实现 ApplicationContextAware 接口不能实现 Spring 容器初始化完成后的注入?

  • 对此自己写了一个类 TestAware 实现 ApplicationContextAware 接口并打印判断是否注入且注入内容是否为 null
    很明显测试结构表明这个是注入成功的

  • 提出一种可行方案 B:
    自己写一个类实现 ApplicationContextAware 接口,并在方法 setApplicationContext 中手动将 ApplicationContext 注入 ApplicationContextHolder.appCtx(同时外层添加异常处理块记录日志)

  • B 方案效果:
    启动直接报错

    java.lang.LinkageError: loader constraint violation: when resolving method “org.apache.axis2.extensions.spring.receivers.ApplicationContextHolder. setApplicationContext(Lorg/springframework/context/ApplicationContext);” the class loader (instance of org/mortbay/jetty/webapp/WebAppClassLoader) of the current class, foo/bar/baz/TestAware, and the class loader (instance of sun/misc/Launcher$AppClassLoader) for resolved class, org/apache/axis2/extensions/spring/receivers/ApplicationContextHolder, have different Class objects for the type org.apache.axis2.extensions.spring.receivers.ApplicationContextHolder. setApplicationContext(Lorg/springframework/context/ApplicationContext); used in the signature

  • 对 B 方案问题分析:
    1)ApplicationContextHolder 在链接的时候出现了错误
    2)ApplicationContextHolder 由 sun.misc.launcher.AppClassLoader 加载
    3)TestAware 由 org.mortbay.jetty.webapp.WebAppClassLoader 加载
    4)ApplicationContextHolder.setApplicationContext(ApplicationContext) 的方法签名类型不同导致方法调用失败
    很显然这是 Web 容器中多 ClassLoader 引起的问题,因为 Web 容器有自己的加载器,但并不完全遵守“双亲委托”模型
    对于 Java ClassLoader 知识此处不表
    根据 Java ClassLoader 机制可知 AppClassLoader 先于 WebAppClassLoader 加载运行,且是后者的 Parent ClassLoader(父加载器),所以可以判断 ApplicationContextHolder 引用的 ApplicationContext 的类加载器是 AppClassLoader;TestAware 引用的 ApplicationContext 的类加载器不是 AppClassLoader,即 TestAware 的 ApplicationContext 的类加载器是 WebAppClassLoader

  • 解决方案
    知道了问题的原因那么只要使 JVM 中的每个类只加载一次且只能被其中一个加载器加载即可。如果所有的 ClassLoader 都遵守“双亲委托”模型,自然不会出现该问题,那么对于像 WebAppClassLoader 这样 ClassLoader 如何保证这一条件呢?
    根据 Jetty WebAppClassLoader 可知其只负责加载 /WEB-INF/classes 下的所有类,并负责加载 /WEB-INF/lib 目录下(不包括/WEB-INF/lib/foo/ 目录)的所有 jar,对于其它的所有类或 jar 以及 WebAppClassLoader 自己都是由 AppClassLoader 加载,那么我们只需要把 Web 工程中的所有的 jar 都放置于 /WEB-INF/lib 目录下即可避免该问题

  • 重新梳理:
    准备用例:

/** * 单独生成 jar 放在除 /WEB-INF/lib 的任意位置 */public class A{  public void hello(C c){    System.out.println("AAAAA");  }}/** * 单独生成 jar 放在 /WEB-INF/lib 下<br> * 或者不生成 jar 直接就在 /WEB-INF/classes 下 */public class B{  public String webRequest(){    hello(new C());  }  public String hello(C c){    System.out.println("BBBBB");    c.hello();    new A().hello(c);  }}/** * 单独生成 jar 放在 /WEB-INF/lib 下<br> * 或者不生成 jar 直接就在 /WEB-INF/classes 下 */public class C{  public void hello(){    System.out.println("CCCCC");  }}

知识点:WebAppClassLoader 不遵守“双亲委托”模型,始终在第一时间由自己尝试加载类,只有在自己搜索路径中不存在类时,才会委托给 Parent ClassLoader(父加载器)
运行分析:
WebAppClassLoader 不管什么时候加载 A,A 始终都是由 AppClassLoader 加载;此时 C 也必须由 AppClassLoader 加载。

记作:A-acl C-acl A-acl.hello(C-acl c)

B 如果此时被 WebAppClassLoader 加载,则 A 会被委托 AppClassLoader 搜索或加载到(因为 /WEB-INF/lib 下没有),则 C 又会由 WebAppClassLoader 重新加载(因为其在/WEB-INF/lib or classes 下)

记作:B-wacl C-wacl A-wacl.hello(C-wacl c)

如果 B-wacl 调用 A.hello(C c) 则会出现类链接异常,因为 B-wacl 中链接到的 A 虽然是 A-acl 但 C 链接到的却是 C-wacl ,而 A-acl 中的链接到的 C 是 C-acl,所以虽然 A B 都只加载一次是唯一的,但这 2 个 C 是不同的,故表现出重复加载同一个类的问题,导致 2 个类(在 JVM 中)表现出不一致

阅读全文
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 母乳不足宝宝不吃奶粉怎么办 4岁宝宝注意力不集中怎么办 孩子上课不专心听讲怎么办 小孩上课不认真听讲怎么办 一年级孩子上课不认真听讲怎么办 打了孩子很自责怎么办 4岁宝宝讲话结巴怎么办 网销客户不说话怎么办 两岁宝宝不说话怎么办? 4岁儿童说话结巴怎么办 3岁宝宝说话结巴怎么办 6岁儿童舌头短怎么办 5岁宝宝说话结巴怎么办 两岁宝宝说话晚怎么办 6岁儿童说话结巴怎么办 2岁宝宝呕吐拉稀怎么办 2岁宝宝突然呕吐怎么办 2岁宝宝呕吐发烧怎么办 2岁宝宝呕吐厉害怎么办 1岁宝宝半夜呕吐怎么办 2岁半宝宝呕吐怎么办 2岁宝宝半夜呕吐怎么办 两岁宝宝一直吐怎么办 原画师老了以后怎么办 孩子不想上学怎么办怎么去说服 嫉妒别人比我好怎么办 三岁宝宝爱打人怎么办 1岁宝宝喜欢打人怎么办 ps图层解锁不了怎么办 沈腾结婚马丽怎么办 延长甲没有纸托怎么办 高考第一志愿没录取怎么办 电子画颜料干了怎么办 数字画颜料干了怎么办 彩砂纸画不好了怎么办 宝宝吃了油画棒怎么办 2岁宝宝不爱刷牙怎么办 两岁宝宝不刷牙怎么办 1岁宝宝不爱刷牙怎么办 3岁宝宝不肯刷牙怎么办 20岁没学历迷茫怎么办