关于现场的一个类加载问题

先看一个服务器的类加载视图(使用Arthas查看很方便,阿里巴巴开源的 Java 诊断工具)

 

当然,Bootstrapclassloader是ExtClassloadered的父级关系,自己也可以自定义classloader在web类加载器下面,说到这里就不得不提jvm的双亲委派机制

双亲委派机制

双亲委派机制是指当一个类加载器收到一个类加载请求时,该类加载器首先会把请求委派给父类加载器。每个类加载器都是如此,只有在父类加载器在自己的搜索范围内找不到指定类时,子类加载器才会尝试自己去加载。

双亲委派模型工作工程:

  1.当Application ClassLoader 收到一个类加载请求时,他首先不会自己去尝试加载这个类,而是将这个请求委派给父类加载器Extension ClassLoader去完成。  

  2.当Extension ClassLoader收到一个类加载请求时,他首先也不会自己去尝试加载这个类,而是将请求委派给父类加载器Bootstrap ClassLoader去完成。  

  3.如果Bootstrap ClassLoader加载失败(在<JAVA_HOME>\lib中未找到所需类),就会让Extension ClassLoader尝试加载。  

  4.如果Extension ClassLoader也加载失败,就会使用Application ClassLoader加载。  

  5.如果Application ClassLoader也加载失败,就会使用自定义加载器去尝试加载。  

  6.如果均加载失败,就会抛出ClassNotFoundException异常。

 

现场遇到当项目部署在服务器上,发现el表达式解析错误,以下为错误堆栈:

Caused by: java.lang.NullPointerException

at java.util.Objects.requireNonNull(Objects.java:203)

at javax.el.CompositeELResolver.add(CompositeELResolver.java:43)

at com.bes.enterprise.web.jasper.el.ELContextImpl.<clinit>(ELContextImpl.java:83)

... 117 more

15:14:08.519|SEVERE|web|_ThreadID=237;_ThreadName=httpWorkerThread-8080-20|Servlet.service() for servlet [jsp] threw exception

java.lang.NoClassDefFoundError: Could not initialize class com.bes.enterprise.web.jasper.el.ELContextImpl

at com.bes.enterprise.web.jasper.compiler.Validator$ValidateVisitor.getJspAttribute(Validator.java:1439)

at com.bes.enterprise.web.jasper.compiler.Validator$ValidateVisitor.checkXmlAttributes(Validator.java:1257)

at com.bes.enterprise.web.jasper.compiler.Validator$ValidateVisitor.visit(Validator.java:890)

at com.bes.enterprise.web.jasper.compiler.Node$CustomTag.accept(Node.java:1543)

at com.bes.enterprise.web.jasper.compiler.Node$Nodes.visit(Node.java:2388)

at com.bes.enterprise.web.jasper.compiler.Node$Visitor.visitBody(Node.java:2440)

发现实例化不了服务器下的

java.lang.NoClassDefFoundError: Could not initialize class com.bes.enterprise.web.jasper.el.ELContextImpl

仔细查看堆栈

javax.el.CompositeELResolver.add(CompositeELResolver.java:47)

这个类路径,应用的juel-api.jar和服务器下的el-api.jar都有,发现两者都调用这个方法,且包名相同,所以会冲突,发现这个jar包是activiti的el实现和tomcat的el实现冲突导致

juel-api-2.2.7.jar ——包含javax.el 包下的一些类

juel-impl-2.2.7.jar ——包含de.odysseus.el 实现类

juel-spi-2.2.7.jar ——包含META-INF/service/javax.el.ExpressionFactory 服务提供资源的定义(如果你的classpath里有多个EL的实现,而你又希望使用JUEL的实现,那么需要调用ExpressionFactory.newInstance() )

再查看调用关系,发现对ExpressionFactory抽象类的调用不同,tomcat的EL调用的是tomcat的org.apache.el.ExpressionFactoryImpl,而juel调用的是 juel-api.jar中的de.odysseus.el.ExpressionFactoryImpl,原来如此,也就是说tomcat的EL和juel都只是满足自己解析表达式的需要,且因为使用了相同的包名(javax.el),不能同时使用,要么只能解析JSP中的EL表达式,要么只能解析Activiti中的EL表达式。

自己有测试将juel-api.jar向上委托父加载器,发现不能强转

 

所以解决方案有是三种 1 去掉juel-spi.jar

                                     2 降低activiti的版本到5.x

                                     3 将服务器下的el-api.jar包名给改掉 

 

 

题外话:看到一篇很不错的

Tomcat ClassLoader详解  原文链接:https://blog.csdn.net/qq_35076190/article/details/115529271

posted on 2021-04-23 15:24  sina-p  阅读(321)  评论(0)    收藏  举报

导航