Maven学习:使用maven-shade-plugin解决依赖包冲突

java 依赖包冲突,使用maven-shade-plugin解决

错误场景详情

1
java.lang.NoSuchMethodError: com.google.common.util.concurrent.MoreExecutors.directExecutor()Ljava/util/concurrent/Executor;

错误原因

出现这样的错误详情一般是由于有下面这样的包依赖情况

1
2
A - B - C(guava version 18)
\ D - C(guava version 23.6-jre)

A:代表我们所开发的当前项目
B和D:代表当前项目所依赖的项目
C:代表当前项目依赖的项目所依赖的项目

由于我们当前所开发的项目A依赖了B和D,B和D又依赖了项目C
我们打包运行项目时,maven只会将一个版本C(guava)打进包内(maven打包遇到相同依赖,最短路径优先,在路径相同时先在pom中声明优先)
比如此时打进包的版本是C(guava version 23.6-jre),那么很有可能在运行B中的一个方法时,调用C的一个方法,这个方法是C(guava version 18)中的一个方法,在C(guava version 23.6-jre)中并不存在,这时候就会报出java.lang.NoSuchMethodError

解决方案

使用maven-shade-plugin将所有B项目依赖的包全部打进B.jar中,并且给guava包的路径重命名为我们的自定义路径\

maven-shade-plugin基本功能

maven-shade-plugin提供了两大基本功能:

1、将依赖的jar包打包到当前jar包(常规打包是不会将所依赖jar包打进来的)
2、对依赖的jar包进行重命名(用于类的隔离,解决包冲突就是使用了这个功能)

解决示例

如下例,就可以在B项目中使用C(guava version 18),只不过import路径变成了我们自定的路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>RpcModule</groupId>
<artifactId>RpcModule</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>

<name>RpcModule</name>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<guava.version>18</guava.version>
</properties>

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>

<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.1</version>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<!-- 给guava包的路径重命名为我们的自定义路径 -->
<configuration>
<relocations>
<relocation>
<pattern>com.google.guava</pattern>
<shadedPattern>shade.com.google.guava</shadedPattern>
</relocation>
<relocation>
<pattern>org.joda</pattern>
<shadedPattern>shade.com.google.joda</shadedPattern>
</relocation>
<relocation>
<pattern>com.google.common</pattern>
<shadedPattern>shade.com.google.common</shadedPattern>
</relocation>
</relocations>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"/>
</transformers>
</configuration>
</execution>
</executions>
</plugin>

</plugins>
</build>
</project>

使用maven-shade-plugin之前

1
2
3
4
5
6
7
8
9
10
import Entity.Request;
import Entity.Response;

import java.io.*;
import java.net.Socket;

import com.google.common.collect.ImmutableMap;

public class SocketClient {
}

使用maven-shade-plugin之后编译后反编译结果

1
2
3
4
5
6
7
8
9
10
import Entity.Request;
import Entity.Response;

import java.io.*;
import java.net.Socket;

import shade.com.google.common.collect.ImmutableMap;

public class SocketClient {
}

最后将打好的包上传至我们的maven仓库,然后再在当前项目A中依赖,就没有依赖冲突了

1
2
A - B - C(guava version 18, shade.com.google.common.collect.ImmutableMap)
\ D - C(guava version 23.6-jre, com.google.common.collect.ImmutableMap)

这样就可以做到将两个不同版本的包都引入使用,由于引入包路径不同,因此也没有冲突