记一次jar包冲突导致项目启动失败的处理过程【java.lang.NoSuchMethodError:javax.servlet.ServletContext】

原创
2019/04/23 18:49
阅读数 3.2K

自从搞明白idea下,Jetty采用main方法启动web项目后,准备大刀阔斧地把其他web项目也改成jetty启动,不幸的是,第一个项目就遇到了问题,这里记录下整个排查流程及处理办法。

1. 异常发生

项目按idea下,Jetty采用main方法启动web项目一文中所述的进行配置后,运行,发现出现了如下异常:

java.lang.NoSuchMethodError: javax.servlet.ServletContext.getJspConfigDescriptor()Ljavax/servlet/descriptor/JspConfigDescriptor;
	at org.apache.jasper.servlet.TldScanner.scanJspConfig(TldScanner.java:158)
	at org.apache.jasper.servlet.TldScanner.scan(TldScanner.java:104)
	at org.apache.jasper.servlet.JasperInitializer.onStartup(JasperInitializer.java:103)
	......

在idea中一查javax.servlet.ServletContext,发现共有4个jar包中有这个类,分别是:

  • javax.servlet-api-3.0.1.jar
  • tomcat-embed-core-9.0.13.jar
  • javax.servlet-api-3.1.0.jar
  • servlet-api-2.4.jar

为什么会有这么多servlet-api的版本呢?主要是因为在当前的项目中,还有其他web模块,由于版本控制没做好,每个web模块使用了不同的servlet-api,此外,还有一些jar包在引入时,一不小心就连带引入了servlet-api的jar包。

所以当务之急,就是找到该web模块中的javax.servlet.ServletContext究竟来自于哪个jar包。

2. 查看类引入路径

为了找到项目中类的加载情况,需要在jvm的启动参数中,添加-verbose:class

3. 启动,查看控制台日志

再次启动,可以看到控制台已经打出了类加载信息了,包括类名、jar包位置:

[Opened C:\Program Files\Java\jdk1.8.0_181\jre\lib\rt.jar]
[Loaded java.lang.Object from C:\Program Files\Java\jdk1.8.0_181\jre\lib\rt.jar]
[Loaded java.io.Serializable from C:\Program Files\Java\jdk1.8.0_181\jre\lib\rt.jar]
[Loaded java.lang.Comparable from C:\Program Files\Java\jdk1.8.0_181\jre\lib\rt.jar]
[Loaded java.lang.CharSequence from C:\Program Files\Java\jdk1.8.0_181\jre\lib\rt.jar]
[Loaded java.lang.String from C:\Program Files\Java\jdk1.8.0_181\jre\lib\rt.jar]
[Loaded java.lang.reflect.AnnotatedElement from C:\Program Files\Java\jdk1.8.0_181\jre\lib\rt.jar]
[Loaded java.lang.reflect.GenericDeclaration from C:\Program Files\Java\jdk1.8.0_181\jre\lib\rt.jar]
[Loaded java.lang.reflect.Type from C:\Program Files\Java\jdk1.8.0_181\jre\lib\rt.jar]
[Loaded java.lang.Class from C:\Program Files\Java\jdk1.8.0_181\jre\lib\rt.jar]
[Loaded java.lang.Cloneable from C:\Program Files\Java\jdk1.8.0_181\jre\lib\rt.jar]
[Loaded java.lang.ClassLoader from C:\Program Files\Java\jdk1.8.0_181\jre\lib\rt.jar]
[Loaded java.lang.System from C:\Program Files\Java\jdk1.8.0_181\jre\lib\rt.jar]
[Loaded java.lang.Throwable from C:\Program Files\Java\jdk1.8.0_181\jre\lib\rt.jar]
......

异常中出现问题的类是javax.servlet.ServletContext,在日志搜索,发现如下记录:

[Loaded javax.servlet.ServletContext from file:/C:/Users/Administrator/.m2/repository/javax/servlet/servlet-api/2.4/servlet-api-2.4.jar]

可以看到,项目中,javax.servlet.ServletContext类是由servlet-api-2.4.jar包引入的。

关于“如何在控制台中搜索”,可以将控制台日志复制一份到文本编辑器中,如notepad++vscode等,然后使用搜索功能。

4. 使用maven定位jar包来源

目前已经知道,项目中的javax.servlet.ServletContext类是由servlet-api-2.4.jar包引入的,但servlet-api-2.4.jar是从哪里引入的呢?项目jar包是由maven来管理的,因此使用mvn命令来查找jar包引入情况,命令如下:

mvn dependency:tree -Dverbose -Dincludes=*:*servlet*

运行之后,控制台输出如下:

[INFO] com.test:test-web:war:0.0.1-SNAPSHOT
[INFO] +- javax.servlet:servlet-api:jar:2.4:provided
[INFO] +- org.eclipse.jetty:jetty-webapp:jar:9.3.2.v20150730:test
[INFO] |  \- org.eclipse.jetty:jetty-servlet:jar:9.3.2.v20150730:test
[INFO] \- org.eclipse.jetty:apache-jsp:jar:9.3.2.v20150730:test
[INFO]    +- org.eclipse.jetty:jetty-server:jar:9.3.2.v20150730:test
[INFO]    |  \- (javax.servlet:javax.servlet-api:jar:3.1.0:test - omitted for duplicate)
[INFO]    \- javax.servlet:javax.servlet-api:jar:3.1.0:test
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS

可以看到,项目中引入的servlet-api版本是2.4,而jetty使用的servlet-api版本是3.1.0,这样就导致了jetty中使用了低版本的servlet-api,从而出现异常。

明白了问题所在,解决就很简单了,只要将项目servlet-api的版本由2.4升级到3.1.0就行了。

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
1 收藏
1
分享
返回顶部
顶部