I took over a set of system with a sense of time, and planned to write a series of articles on the reconstruction and problems encountered, so that the old trees would grow new branches and review some actual combat techniques to share with everyone. Maven project Jar package management mechanism, conflict resolution

Knowledge background

Jar package conflict is inevitable in software development process, so how to quickly locate the source of conflict, understand the process caused by conflict and the underlying principle, is a required course for every programmer. It’s also an opportunity to be more productive, handle interviews, and stand out from the crowd.

In practice, Jar package conflicts can be intuitively felt in the following ways:

  • Program is thrownjava.lang.ClassNotFoundExceptionThe exception;
  • Program is thrownjava.lang.NoSuchMethodErrorThe exception;
  • Program is thrownjava.lang.NoClassDefFoundErrorThe exception;
  • Program is thrownjava.lang.LinkageErrorAbnormal, etc.;

This is intuitive, but there are also hidden exceptions, such as program execution not being as expected. In the following sections, we will examine the handling mechanism of Jar packages in Maven projects and the causes of conflicts.

Maven Jar package management mechanism

Understanding Jar conflicts in the Maven project requires understanding how Maven manages Jar packages. This refers to Maven features such as dependency passing, shortest path first, first claim, and so on.

Dependency transfer principle

When A dependency is introduced in A Maven project, the dependency usually introduces the JAR package of B, which may also introduce the JAR package of C. This way, when you add A dependency to the POM.xml file, Maven automatically adds all related dependencies for you.

For example, in the Spring Boot TAB, when spring-boot-starter-web is introduced:

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
</dependency>
Copy the code

Maven’s dependency structure might look like this:

The above relationship can be understood as transitivity of dependence. When a dependency needs to be supported by another dependency, Maven adds the corresponding dependency to the project in turn.

The advantage of this is that it is very easy to use, and you do not have to go through the dependent Jar packages yourself. The downside is Jar package collisions, which we’ll cover later.

Shortest path first

Dependency link 1: Determines which dependency to introduce (two conflicting dependencies) based on the length of the dependency path.

For example:

A dependent link: A - > X - > - > Y Z (21.0) rely on link 2: B - > Q - > Z (20.0)Copy the code

Both A and B dependencies were introduced in the project, and they indirectly introduced Z dependencies. However, due to the short dependency link of B, version Z(20.0) finally took effect. This is the shortest path first principle.

At this point, if the 21.0 and 20.0 versions of Z are significantly different, Jar package conflicts will occur.

Be the first to declare priority

If two dependent paths are the same, the shortest path priority cannot be determined. In this case, the shortest path priority principle is used. That is, the shortest path priority is selected first.

For example:

Dependent link A: A -> X -> Z(21.0) Dependent link B: > Q -> Z(20.0)Copy the code

Both A and B ultimately depend on Z, where A’s declaration (the order introduced in poM) takes precedence over B, and Z for conflicts takes precedence over Z(21.0).

If Z(21.0) is backward compatible with Z(20.0), Jar package conflicts do not occur. However, if the B declaration comes first, Jar package conflicts can occur.

Cause of Jar package conflict

Maven’s three principles for maintaining Jar packages have been described above, and you have already seen what Jar conflicts can occur with each of these principles. Here’s another comprehensive example.

For example:

Dependent link 1: A -> B -> C -> G21(Guava 21.0) dependent link 2: D -> F -> G20(Guava 20.0)Copy the code

Assuming that both A and D dependencies are introduced into the project, the dependency transfer mechanism and the default dependency adjustment mechanism are adopted (first: the one with the shortest path is preferred; The G21 Jar package will not be referenced, but the G20 version will be introduced by default.

If a method in C uses a new method (or class) from the G21 version, G21 was not introduced due to Maven’s processing. In this case, the program will throw a ClassNotFoundException when calling the corresponding class and a NoSuchMethodError when calling the corresponding method.

Check and locate Jar conflicts

To view Maven Dependencies, right-click in the POm. XML file and select Show Dependencies.

After execution, the result is the same as the original Spring-boot-Web effect. You can clearly see which dependencies are used, their hierarchy, and if there are any conflicting JAR packages. Conflicts are highlighted in red, along with which version Maven selected by default.

If your IDEA version does not have Maven management plug-ins by default, you can also install Maven Helper to help you analyze Jar conflicts.

After installing the plug-in and restarting, open the POM.xml file and see the result analysis of Jar package conflicts in the Dependency Analyzer view below:

At this point, it becomes clear which Jar packages are in conflict. At the same time, you can right click the conflicting Jar package and execute “Exclude” to Exclude the Jar package. The attributes to Exclude the Jar package will be automatically added to pom.xml.

Plugins such as Maven Helper are available for local environments, but not so convenient for pre-production or build environments. At this point you can use the MVN command to locate the highlighted details.

Run the following MVN commands:

mvn dependency:tree -Dverbose
Copy the code

Be careful not to omit -dverbose, otherwise ignored packages will not be displayed.

The result is as follows:

In this way, it is also clear which Jar packages are in conflict.

How to unify Jar package dependencies

As shown in the screenshot above, if a project consists of N multiple sub-projects, there may be dependencies between the projects, and Jar package conflicts are inevitable. In this case, the parent POM can be used to manage the unified version once and for all.

It is common practice to declare the versions of all relevant dependent Jar packages as much as possible in the parent module’s POM file and simply reference (without specifying the version) that artifact in the child POM.

For example, defining a version of Lombok in the parent pom.xml:

<dependencyManagement> <dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> The < version > 1.18.10 < / version > < / dependency > < / dependencies > < / dependencyManagement >Copy the code

This can be defined in a sub-module as follows:

<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
</dependencies>
Copy the code

In this way, the same version is used in all child modules.

Methods to resolve Jar package conflicts

Here’s how to resolve Jar conflicts in several scenarios based on the Maven project:

  • Default handling for Maven: In this way, remember the basic principles of Maven’s dependency tuning mechanism: nearest path first and first declaration first.
  • Elimination: As mentioned in the Maven Helper example above, you can pass conflicting Jar packages in pom.xmlexcludeTo exclude;
  • Version locking: If the project depends on many versions of the same Jar package, it is very troublesome to exclude them one by one. This method has the highest priority according to Maven’s basic principles for handling Jar packages. This approach generally follows the same approach we described above to unify Jar package dependencies.

The nature of Jar package conflicts

Maven’s principles for resolving Jar conflicts in projects and solutions at the field level are described above, but the nature of Jar conflicts is not covered. The nature of Jar conflicts has been explained in detail in the article from Jar conflicts to class loading mechanisms. Here’s an overview of some of the key points.

The nature of Jar package conflicts: Java applications do not load the right classes for some reason, causing them to behave differently than expected.

There are two specific cases:

  • Case 1: The project relies on multiple versions of the same Jar package and the wrong version is selected;
  • The same class appears in a different Jar, causing the JVM to load the wrong class.

The first scenario, which is the focus of this article, is that multiple Jar package versions are introduced, and different Jar package versions have different classes and methods. [Root@maven] [root@maven] [root@maven] [root@maven] [root@maven] [root@maven]

In case two, the same class appears in different Jar packages (described in detail in the previous article). This is due to the fact that the same JVM class loader will only load the same class once, and once a class is loaded, classes with fully qualified names will not be loaded, resulting in Jar package conflicts.

In the second case, you might not even be aware of it if a class conflict had not thrown the exception, so it’s a little trickier. This situation can be solved by analyzing the priority and loading path of different loaders and the file loading sequence of the file system as described above.

summary

In addition to the above methods, there are a number of tricks to check for class conflicts, such as using the search function provided by the IDE to directly search for classes that throw exceptions to see if there are more than one, and if they are using the expected version. These skills need to be explored and accumulated in the process of practice.

In short, no matter how big the project is or how complex the dependency is, there will always be a trace if you keep in mind the causes of the conflict and several ways to resolve the conflict and carefully analyze it. By the end of this article, give it a try, and you might be able to stand out as a Jar conflict-buster on your team.

About the blogger: Author of the technology book SpringBoot Inside Technology, loves to delve into technology and writes technical articles.

Public account: “program new vision”, the blogger’s public account, welcome to follow ~

Technical exchange: Please contact the weibo user at Zhuan2quan

\