遇到maven的依赖冲突三种主要解决方法:
1、当冲突两者其中一方兼容另外一方时,通过exclusions和exclusion标签解决。
2、当冲突两者互不兼容时,使用maven-shade-plugin管理插件解决。
3、覆盖jar包中的代码并结合反编译软件解决。
问题:第三方给的SDK加密包与我们工程中的这两个包的 org.bouncycastle.asn1.x9.X9IntegerConverter类冲突。
A.jar == national.secrets-2.0.0.jar
B.jar == bcprov-jdk15on-1.68.jar、bcpkix-jdk15on-1.68.jar
下文统一用A、B.jar代替。
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.68</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.68</version>
</dependency>
代码报错:
Caused by: java.lang.NoSuchMethodError: org.bouncycastle.asn1.x9.X9IntegerConverter.getByteLength(Lcom/xmsme/national/secrets/math/ec/ECFieldElement;)I
at com.xmsme.national.secrets.math.ec.ECPoint$Fp.getEncoded(ECPoint.java:203)
at com.xmsme.national.secrets.SM2Utils.encrypt(SM2Utils.java:73)
at com.a.sunis.b.util.RequestUtil.getResData(RequestUtil.java:50)
at com.a.sunis.b.controller.QueryInfoController.judgementDocuments(QueryInfoController.java:29)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
初步判断:
1.对方给的A.jar包有问题。
2.我们项目中的B.jar包和A.jar包冲突了。
首先新建一个项目,导入A.jar,把在我们项目中报错的那行代码放到新项目中,发现运行正常。
所以排除第一种情况对方给的A.jar包有问题。
既然是jar包冲突,我就用Maven Helper插件,进行依赖分析。
结果如图,并没有发现A.jar和其他jar存在冲突的问题。
然后,我就自己胡搞,把报错的那几个类从A.jar包class文件中反编译成java类,放到自己项目中,包名类名都保持一致,
看看能否发现问题。(还是经验不足,不然应该能直接知道是ECPoint类调用X9IntegerConverter类getByteLength方法这块有问题。)
然后就打断点,在这几个源码类中一行一行往下运行。
最终发现在ECPoint类里发现,这个方法的参数类型有问题。
那么为什么它这个参数类型不对呢,是调用的时候填的这个this.x参数类型不对,还是X9IntegerConverter.getByteLength的形参类型有问题。
答案是X9IntegerConverter.getByteLength的形参类型有问题,但并不是A.jar包中的X9IntegerConverter.getByteLength的形参类型有问题,而是B.jar包中的X9IntegerConverter.getByteLength的形参类型有问题。也不能说有问题,只是说A.jar包的ECPoint类应该要去调用A.jar包里的org.bouncycastle.asn1.x9.X9IntegerConverter类的getByteLength方法,但是因为 某些原因 去调用了B.jar包里的org.bouncycastle.asn1.x9.X9IntegerConverter类的getByteLength方法,但是这两个方法的形参类型不同,如下图,所以才发生了错误。
这个某些原因我暂时认为是在pom.xml中,B.jar包的依赖引入在A.jar包上面,maven的仲裁机制会按照路径最短原则,或者是最先声明原则,所以在A.jar包想调用同包名同类名的X9IntegerConverter类的getByteLength方法时会选择B.jar包中的X9IntegerConverter类的getByteLength方法。(这个某些原因的解释有待商榷,仅供参考。只是我暂时这么理解的。)
在我中间胡搞覆盖源码的时候把ECPoint,X9IntegerConverter 类从源码中提取出来,就报这么一个错。
org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.ExceptionInInitializerError
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1055)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:497)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:584)
at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:74)
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:129)
at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:92)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
at com.a.sunis.support.XssSqlFilter.doFilter(XssSqlFilter.java:28)
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
at com.a.sunis.support.RepeatFlowFilter.doFilter(RepeatFlowFilter.java:28)
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:97)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
at io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:84)
at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
at io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:68)
at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
at io.undertow.servlet.handlers.RedirectDirHandler.handleRequest(RedirectDirHandler.java:68)
at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:111)
at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46)
at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64)
at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:60)
at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:77)
at io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:269)
at io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:78)
at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:133)
at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:130)
at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48)
at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:249)
at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:78)
at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:99)
at io.undertow.server.Connectors.executeRootHandler(Connectors.java:390)
at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:836)
at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:2019)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1558)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1423)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.ExceptionInInitializerError
at com.xmsme.national.secrets.math.ec.ECCurve$Fp.<init>(ECCurve.java:46)
at com.xmsme.national.secrets.SM2.<init>(SM2.java:62)
at com.xmsme.national.secrets.SM2.Instance(SM2.java:35)
at com.xmsme.national.secrets.SM2Utils.encrypt(SM2Utils.java:64)
at com.a.sunis.b.util.RequestUtil.getResData(RequestUtil.java:50)
at com.a.sunis.b.controller.QueryInfoController.judgementDocuments(QueryInfoController.java:29)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:105)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:878)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:792)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040)
... 61 more
Caused by: java.lang.SecurityException: class "org.bouncycastle.asn1.x9.X9IntegerConverter"'s signer information does not match signer information of other classes in the same package
at java.lang.ClassLoader.checkCerts(ClassLoader.java:898)
at java.lang.ClassLoader.preDefineClass(ClassLoader.java:668)
at java.lang.ClassLoader.defineClass(ClassLoader.java:761)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:468)
at java.net.URLClassLoader.access$100(URLClassLoader.java:74)
at java.net.URLClassLoader$1.run(URLClassLoader.java:369)
at java.net.URLClassLoader$1.run(URLClassLoader.java:363)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:362)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at com.xmsme.national.secrets.math.ec.ECPoint.<clinit>(ECPoint.java:18)
... 78 more
主要就是这句话给了我改包名的灵感。
Caused by: java.lang.SecurityException: class “org.bouncycastle.asn1.x9.X9IntegerConverter”'s signer information does not match signer information of other classes in the same package
我就把X9IntegerConverter类改了包名,并且把A.jar用到X9IntegerConverter类的ECPoint类也改了导入的X9IntegerConverter类的包名。这样我就覆盖了A.jar包中X9IntegerConverter和ECPoint类,让ECPoint类能调用到正确的X9IntegerConverter类的getByteLength方法。然后我重新启动项目,发现不报错了。
这就是我乱搞之后想到的第一种解决办法。(这种可以用,但是不太保险不太友好,因为你不知道A.jar包内部的类还有没有其他地方用到了这个X9IntegerConverter类。但是可以通过一些方式来保证这种方式的可靠性。比如,1、用class反编译软件,像jd-gui这种,打开A.jar,搜索X9IntegerConverter,这样就可以看到所有引用X9IntegerConverter类的地方,这种方式来保证我们自己手动覆盖代码的可靠性。2、在自己项目中把用到A.jar包的地方的代码都过一遍,看看是否有问题,多测测。3、如果A.jar包中类较少的话可以从IDEA中翻翻,还有C类用到X9IntegerConverter类,就把C类也拿到自己项目中改导入X9IntegerConverter类的包名进行覆盖。)
然后我又去查资料看看有没有其他解决办法,我的问题主要是jar隔离这个方向的。
期间试了exclusions和exclusion标签来解决,但是不管用。
jar包隔离的几种方式,参考了这位大佬的博客。https://blog.csdn.net/qq_37527921/article/details/102839369
1、spring boot方式,统一管理各个组件版本,简洁高效,但遇到必须使用不同版本jar包时,就不行了
2、OSGI技术,用容器对jar包进行暴露和隔离,实际上是通过不同classload加载类来达到目的,真正的jar包隔离,还能做模块化,服务热部署,热更新等,缺点就是太重了。
3、sofa-ark 用FatJar技术去实现OSGI的功能,jar包隔离原理上跟osgi一致,不过基于fat jar技术,通过maven 插件来简化复杂度,比较轻量,也支持服务热部署热更新等功能。
4、shade 也有maven插件,通过更改jar包的字节码来避免jai包冲突,jar包冲突的本质是类的全限定名(包名+类名)冲突了,通过全限定名不能定位到你想用的那个类,maven-shade插件可以更改jar包里的包名,来达到解决冲突的目的。
5、自己定义classload,反射调用冲突方法,代码量太大,不通用,但是会帮助理解上面组件的原理。
然后我排除了第一个,用了第五种的方式,但我发现对我这种情况不好用。它编译的时候会报错。
然后就去用了第四种方式maven插件maven-shade-plugin.
这种方式和我第一种解决方式一样,本质就是改包名。
只不过这种方式相当于我第一种方式覆盖代码改包名+反编译软件查找X9IntegerConverter类改它用到的地方的结合体。
而且更可靠友好,简单快捷。
1、新建一个工程,在pom.xml导入A.jar
<dependencies>
<!-- 只留一个依赖,其他依赖一定删掉,不然后面会有很多坑>
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter</artifactId>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-test</artifactId>-->
<!-- <scope>test</scope>-->
<!-- </dependency>-->
<dependency>
<groupId>com.xmsme</groupId>
<artifactId>national.secrets</artifactId>
<version>2.0.0</version>
</dependency>
</dependencies>
这个版本之前用的2.4.3成功踩坑。把版本换成3.2.1就好了。
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
Failed to execute goal org.apache.maven.plugins:maven-shade-plugin:2.4.3:shade (default) on project test_receive_request_param_format: Error creating shaded jar: null
2、添加maven-shade-plugin插件。
<build>
<plugins>
<!-- 省略maven-compiler-plugin、spring-boot-maven-plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createDependencyReducedPom>true</createDependencyReducedPom>
<relocations>
<!-- 多个包需要替换使用多个relocation,和exclude的用法类似。-->
<relocation>
<!-- pattern 为包名匹配的前缀 -->
<pattern>org.bouncycastle</pattern>
<!-- shadedPattern 为想要把包名替换成什么的前缀 -->
<shadedPattern>shaded.org.bouncycastle</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
3、刷新maven工程,打包。
发现X9IntegerConverter类的包路径已经更改。
ECPoint类的导入X9IntegerConverter类的包路径也已经修改。
4、安装jar包到maven仓库中。
mvn install:install-file "-Dfile=package-demo-0.0.1-SNAPSHOT.jar" "-DgroupId=com.xmsme" "-DartifactId=shaded.national.secrets" "-Dversion=2.0.0" "-Dpackaging=jar"
5、在我们原来的项目中去掉
改为
<dependency>
<groupId>com.xmsme</groupId>
<artifactId>shaded.national.secrets</artifactId>
<version>2.0.0</version>
</dependency>
6、刷新maven,maven clean,重新启动项目调用接口。
7、如果打完的jar包安装到本地,依赖到项目中,项目运行的时候报这个错。(踩坑)
java.lang.SecurityException: Invalid signature file digest for Manifest main attributes
就用压缩软件在maven仓库中打开刚才安装到本地的jar包,打开META-INF
目录,将*.SF,*.DSA,*.RSA
文件都删除。
看别的大佬说的是因为某些包的重复引用,导致打包之后的META-INF
的目录下多出了一些*.SF,*.DSA,*.RSA
文件。
遇到maven的依赖冲突三种主要解决方法:
1、当冲突两者其中一方兼容另外一方时,通过exclusions和exclusion标签解决。
2、当冲突两者互不兼容时,使用maven-shade-plugin管理插件解决。
3、覆盖jar包中的代码并结合反编译软件解决。