从Tomcat 9.0.87迁到9.0.88后WstxInputFactory类找不到,求项目内修复
问题背景
将Struts Web项目从Tomcat 9.0.87迁移至9.0.88后,启动站点无法访问(页面空白),日志抛出核心错误:
java.lang.NoClassDefFoundError: Could not initialize class org.apache.jasper.compiler.EncodingDetector Caused by: java.lang.ExceptionInInitializerError: Exception javax.xml.stream.FactoryConfigurationError: Provider com.ctc.wstx.stax.WstxInputFactory not found
已知将包含com.ctc.wstx.stax.WstxInputFactory的woodstox jar包放入Tomcat的lib目录可解决问题,但需仅通过修改项目本身完成修复。
结合Tomcat 9.0.88的Jasper模块变更说明:
Prevent the web application's ClassLoader from being pinned by the JSP compiler if an application uses a custom XMLInputFactory. Based upon a suggestion from Simon Niederberger. (schultz)
该变更导致Jasper编译模块不再使用应用类加载器,转而使用系统类加载器加载XMLInputFactory,因此项目WEB-INF/lib中的woodstox jar无法被Jasper识别。
项目内修复方案
方案1:强制Jasper使用JDK默认XMLInputFactory
在项目的web.xml中配置JSP Servlet的初始化参数,指定使用JDK自带的XMLInputFactory实现:
<servlet> <servlet-name>jsp</servlet-name> <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class> <init-param> <param-name>xmlInputFactory</param-name> <param-value>com.sun.xml.internal.stream.XMLInputFactoryImpl</param-value> </init-param> <load-on-startup>3</load-on-startup> </servlet>
此方案无需修改项目依赖,适合不需要自定义XMLInputFactory的场景。
方案2:让Jasper识别项目中的自定义XMLInputFactory
如果项目必须使用woodstox的WstxInputFactory,可通过以下两种方式实现:
方式A:通过系统属性指定实现类
在项目的ServletContextListener初始化方法中添加系统属性设置,明确指定XMLInputFactory的实现类:
public class AppContextListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { System.setProperty("javax.xml.stream.XMLInputFactory", "com.ctc.wstx.stax.WstxInputFactory"); } @Override public void contextDestroyed(ServletContextEvent sce) { // 可选:销毁时清除属性 System.clearProperty("javax.xml.stream.XMLInputFactory"); } }
记得在web.xml中注册该监听器:
<listener> <listener-class>com.yourpackage.AppContextListener</listener-class> </listener>
方式B:使用服务提供者机制
在项目的src/main/resources/META-INF/services目录下创建文件javax.xml.stream.XMLInputFactory,文件内容为:
com.ctc.wstx.stax.WstxInputFactory
该文件会让类加载器自动识别并加载指定的XMLInputFactory实现,确保Jasper能通过应用类加载器找到它。
内容的提问来源于stack exchange,提问作者rv_767

