Grails 3项目又一Gradle依赖问题:Jasper Reports插件异常
这种JAXP实现被第三方jar抢占的问题在Grails里真的很常见,尤其是当项目里引入了Oracle JDBC驱动的时候——我之前帮同事排查过几乎一模一样的情况,给你几个靠谱的解决思路:
排除Oracle JDBC依赖中的xmlparserv2模块
大多数Oracle JDBC驱动包(比如你用到的xmlparserv2-12.1.0.2.jar)都会自带JAXP的实现,这个包会通过META-INF/services/javax.xml.parsers.DocumentBuilderFactory文件抢占默认实现。你可以在build.gradle里直接排除这个依赖:dependencies { // 假设你的Oracle JDBC依赖是这样的,根据实际情况调整 runtime('com.oracle:ojdbc8:12.1.0.2') { exclude group: 'com.oracle', module: 'xmlparserv2' } // 确保你的Jasper插件依赖的xercesImpl被正确引入 compile 'xerces:xercesImpl:2.12.1' }排除后,Grails会自动使用xercesImpl的JAXP实现,这是最彻底的解决方式。
强制指定JAXP实现类的系统属性
如果因为某些原因不能排除xmlparserv2,你可以通过系统属性直接指定要用的DocumentBuilderFactory实现类。有两种方式:- 在
grails-app/conf/application.groovy中添加启动时的系统属性设置:// 放在配置文件开头,确保启动时优先生效 System.setProperty("javax.xml.parsers.DocumentBuilderFactory", "org.apache.xerces.jaxp.DocumentBuilderFactoryImpl") - 在应用启动脚本中添加JVM参数:
-Djavax.xml.parsers.DocumentBuilderFactory=org.apache.xerces.jaxp.DocumentBuilderFactoryImpl
这种方式会绕过JDK的服务发现机制,直接强制使用你指定的实现类。
- 在
在插件代码中显式指定实现类(如果插件由你维护)
如果这个Jasper插件是你自己开发的,可以在调用DocumentBuilderFactory的地方,直接指定实现类和类加载器,避免受应用环境影响:// 替换原来的DocumentBuilderFactory.newInstance() DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance( "org.apache.xerces.jaxp.DocumentBuilderFactoryImpl", Thread.currentThread().getContextClassLoader() );这种方式最稳妥,插件的行为不会受外部应用的依赖影响。
验证解决效果
不管用哪种方法,都可以在代码里加一行打印来确认:System.out.println("当前DocumentBuilderFactory实现类:" + DocumentBuilderFactory.newInstance().getClass().getName());如果输出是
org.apache.xerces.jaxp.DocumentBuilderFactoryImpl,就说明问题解决了。
内容的提问来源于stack exchange,提问作者Jay

