ContextClassLoader使用不当导致的"奇怪"现象

最近从一位同学那得知了一个奇怪的异常现象(和classloader相关), 问题一直没有解决, 所以趁周末有空搭建测试环境研究一下:

问题现象

该应用是服务型应用, 如果应用启动时初始化所有的spring bean, 那么会有如下的异常,反之运行时一点一点初始化spring bean则没有任何问题。(所以应用在线上没有使用fail fast模式,有一定风险)
_2015_08_11_6_58_17

个人分析

  • 异常比较底层, 所以把抛出异常的class(DirContextURLStreamHandler)反编译看了下。如下:
    _2015_08_11_7_10_52
  • 先了解了下该class的结构和用途:该类中clBindings和threadBindings是普通的map对象, 当执行ServletContext.getResource(resourceName).openConnection()方法后DirContextURLStreamHandler的get方法都会调用(基本可认为只要加载web应用下文件就会使用). 为什么这个方法调用会和异常堆栈联系在一起? 这里简单讲解下tomcat的加载应用中资源的逻辑, 当我们使用ServletContext.getResource获取该资源URL时,你可能看到如下的结果, 因为tomcat 使用了Java命名系统接口(jndi)来命名/操作应用下的资源:
    _2015_08_08_12_03_37
  • jndi这个协议的Handler是通过以下语句设置的(WebappLoader.java中):
    URL.setURLStreamHandlerFactory(new DirContextURLStreamHandlerFactory);
  • 而DirContextURLStreamHandlerFactory实现也比较简单,如果url的协议是jndi, 就由DirContextURLStreamHandler(这也是最终抛出异常的类)来进行处理。
    _2015_08_11_8_12_47

从异常中发现,加载webx中的uri.xml时抛出了异常

定位问题

_2015_08_11_7_10_52

  • 尝试在class中增加了断点。当抛出异常时, 上图中115行的result为nulll, 正常情况下result是有值的。
    出现异常时clBindings中包含的key是org.jboss.web.tomcat.tc5.WebCtxLoader$ENCLoader, 而currentCL(上下文classloader)是org.jboss.mx.loading.UnifiedClassLoader3, 而导致查询不到结果. 所以我开始怀疑,是否业务代码中将上下文的classloader给改掉了。而导致这里查询不到DirContext(web目录上下文对象).
  • 用btrace打印了所有调用Thread.setContextClassLoader(ClassLoader classloder)的点, 并且该方法参数的class是org.jboss.mx.loading.UnifiedClassLoader3的线程堆栈. 日志打印出来后,内容很多,但是稍微过滤后(堆栈前5 frame中包含alibaba or taobao关键字),可以最终只剩下几个可疑class:
  • com.taobao.hsf.route.strategy.groovy.GroovyRouteRuleParser.parse(GroovyRouteRuleParser.java:103(正常)
    _2015_08_11_8_25_03

这里在操作ContextClassLoader逻辑是: 取出->更改->还原(finally语句块)

  • com.taobao.agoo.open.OpenServiceFactory.init(OpenServiceFactory.java:57)(有问题)
    _2015_08_11_8_26_05

这里在操作ContextClassLoader逻辑是: 取出->更改

解决问题

  • 很明显了,只要在OpenServiceFactory.java中增加"还原"的操作就可以了。增加"还原"操作后,应用启动初始化所有bean正常, 且无异常抛出。

问题还原

  • OpenServiceFactory更改了ContextClassLoader,但是没有还原原有的ContextClassLoader这种写法很少见(我不确定代码这样写的原有),但这里确实导致了该应用启动时抛出的异常。
  • 应用启动抛出异常 真实的原因是 OpenServiceFactory的初始化比较早,导致后面初始化home.webx.xml(还有其他的xml)出现了加载不到的异常。如果OpenServiceFactory初始化比较晚,并且应用不需要加载web下的资源文件,那么也不会有问题。
  • 那为什么应用启动时,不初始化OpenServiceFactory就没有问题呢?因为该应用是服务型的应用,启动时已经初始化过了所需要的web目录下资源文件(例如: webx.xml, pipeline.xml等), 没有其他的资源文件需要加载(即使影响也肯能只影响修改了ContextClassLoader的线程), 所以不会导致这个问题出现。

classloader引起的问题很少见,问题出现后也不太容易排查和解决,这个场景相对是比较简单,只是这个异常比较"奇怪"而已

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值