Before we talk about maven dependency conflicts, we need to talk about Maven dependency delivery.

Depend on the transfer

The current project introduces a dependency whose dependencies are also introduced into the project. To be more precise, Maven parses the POM of direct dependencies and introduces the necessary indirect dependencies into the current project in the form of transitive dependencies.

Why ‘necessary indirect dependence’? This is because not all indirect dependencies are introduced. That brings us to Maven’s scope.

Depend on the range

Maven introduces dependencies. Instead of copying the JAR packages into the project, maven downloads the JAR packages to the local repository and then introduces the specific JAR packages into the project by specifying the classpath. Maven manages three sets of classpath: compile classpath, test classpath, and run classpath.

Maven has three classpath dependencies. Maven has three classpath dependencies:

  • Compile: compile-dependent scope. This applies to all classpath. Example: the spring – the core
  • Test: Tests the dependency scope. Only valid for test CLASspath. Example: junit
  • Provided: Dependency scope has been provided. This works for compile and test classpath. Example: the servlet API
  • Runtime: Run-time dependency scope. This works for testing and running classpath. Example: JDBC driver
  • System: system dependency range. This works for compile and test classpath. Explicitly specified by systemPath.
  • Import: Imports the dependency scope. Classpath will not be affected.

In addition to controlling the classpath, dependency scopes also affect dependency delivery. If A depends on B and B depends on C, then A is the first direct dependence on B. B is the second direct dependence on C. A is transitive dependent on C. The conclusion is that the scope of the first direct dependence and the second direct dependence determine the scope of transitive dependence.

Use this table from Maven in Action to illustrate:

The first one is directly dependent on the second one

compile

test provided runtime

compile

compile





runtime

test

test





test

provided

provided

provided provided
runtime

runtime





runtime

The first column is the first direct dependency, the first row is the second direct dependency, and the middle represents the transitive dependency range.

Dependent conflict and dependent mediation

It is because of dependency transfer that it brings the possibility of dependency conflict. Such as A->X(1.0), A->B->X(2.0). A depends directly on version 1.0 of X, while A depends on B on version 2.0 of X. The dependency X in B is also passed to project A if the dependency scope is appropriate. The versions of the two X’s are inconsistent, creating dependency conflicts.

Maven uses a set of rules to mediate dependencies instead of directly prompting errors when dependency conflicts occur. There are two rules:

  1. The closest path is preferred.
  2. First declaration is preferred.

Dependency paths refer to lengths from project to dependency, such as A->X(1.0) is 1 in length and A->B->X(2.0) is 2 in length, so version 1.0 of X will eventually be used.

What if they take the same path? For example, if A->B->X(2.0) and A->C->X(3.0) are both of length 2, which one will be used? Which brings us to the second rule, which is whichever is declared first.

In most cases, maven’s automated dependency mediation will help us solve the problem. But sometimes we have to handle dependency conflicts manually. The conflict may not be different versions of the same dependency (which can be resolved by dependency mediation), but two dependencies that cannot occur at the same time. For example, slf4J-log4j and logback dependencies cannot be used together, but maven does not align them because their coordinates are different. At this point we need to manually remove dependencies.

Eliminate dependence on

The following example is an example of excluding dependencies without specifying the version:

Alibaba </groupId> <artifactId>dubbo</artifactId> <version>2.5.3</version> <exclusions> <exclusion> <groupId>org.springframework</groupId> <artifactId>spring</artifactId> </exclusion> </exclusions> </dependency>Copy the code

If there are many of the same indirect dependencies that need to be excluded, it can be quite troublesome.

Checking for dependency conflicts

Because Maven uses dependency mediation when dependency conflicts occur, there is no hint. So how do we check that? There are two ways to do this.

The first is to use MVN Dependency: tree-dverbose to list all of the project’s dependencies as well as transitive dependencies. For duplicate and conflicting dependencies, omitted for duplicate and omitted for conflict with X.X.X.

The second method is to use Maven’s Enforcer plug-in. Add the following to the project POM:

<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-enforcer-plugin</artifactId> <version>1.4.1</version> <executions> <id>enforce</id> <configuration> <rules> <dependencyConvergence/> </rules> </configuration> <goals> <goal>enforce</goal> </goals> </execution> </executions> </plugin> </plugins> </build>Copy the code

If there is a dependency conflict when compiling with Maven, there will be an error message:

[ERROR] Dependency convergence ERROR for org.slf4j: slf4J-API :1.6.1 Paths to Dependency are: + - org. Myorg: my - project: 1.0.0 - the SNAPSHOT + - org. Slf4j: slf4j - jdk14:1.6.1 + - org. Slf4j: slf4j - API: 1.6.1 and + - org. Myorg: my - project: 1.0.0 - the SNAPSHOT + - org. Slf4j: slf4j - nop: 1.6.0 + - org. Slf4j: slf4j - API: 1.6.0Copy the code

The resources

  • Maven in Action
  • Maven implementation relies on “global exclusion” – Liuyongpo’s personal space – open source Chinese community
  • Maven dependency exclusion disallows dependency transfer and cancels dependency

In this paper, the independent blog: tell me about maven rely on conflict, rely on conciliation, depend on the scope of transfer and rely on | wood of Chinese fir blog