Maven dependency management.

preface

Maven is really easy. It is estimated that you can learn the basic part in a few days by taking a little time every day during your work, so there is really no good feeling about this part.

This article starts with the basics of dependency management and then consumes it with a simple example.

Basic knowledge of

coordinates

Analogous to plane geometry in mathematics, coordinates (x, y), any coordinate can uniquely identify a point in the plane. This point corresponds to the files of.jar,.war, etc. Maven uses elements such as groupId, artifactId, Version, Packaging, and classifier to compose its own coordinates and define a set of such rules. As long as the correct coordinate element is provided, Maven can find the corresponding component.

Coordinate elements:

  • GroupId: Defines the actual project to which the current Maven project belongs.
  • ArtifactId: Defines a Maven project (module) in the actual project.
  • Packaging: Defines how Maven projects are packaged. Jar, WAR, POM. The default is JAR.
  • Version: Defines the current version of the Maven project.
  • Classifier: Distinguishes artifacts with different contents built from the same artifact.

Here’s an example I often use:

<project>
  ...
  <groupId>org.example</groupId>
  <artifactId>demo5</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>
  ...
</project>
Copy the code

The component name corresponds to the coordinates. The general rule is: artifactId-version[-classifier]. Packaging.

Rely on the statement

<project> ... <dependencies> <dependency> <groupId> Actual project </groupId> <artifactId> module </artifactId> <version> version </version> <type> Dependency type </type> <scope> Dependency scope </scope> <optional> Dependency optional </optional> <! -- Exclusions > <exclusion> <groupId>... < / groupId > < artifactId >... </artifactId> </exclusion> </exclusions> </dependency> <dependencies>... </project>Copy the code
  • GroupId, artifactId, Version: base coordinates of dependencies.
  • Type: indicates the dependency type, corresponding to the packaging of the project. This parameter is not required.
  • Scope: the scope of the dependency.
  • Optional: Indicates whether the tag dependency is optional.
  • Exclusions: Used to exclude transitive dependencies.

Depend on the range

Dependency scopes are used to control dependencies and the three classpath types (compile classpath, test classpath, run classpath). Maven has several dependency scopes:

  • **compile:** Compile dependent scope. If not specified, the dependency scope is used by default. Maven dependencies that use this scope are valid for compiling, testing, and running all three classpath types. The classic example is Spring-Code, which is used at compile, test, and run time.
  • Test: Tests the dependency scope. Maven dependencies that use subdependency scopes are valid only for test classpath and will not be available when compiling main code or running project use. The typical example is Jnuit, which is only needed to compile test code and run tests.
  • ** Provided :** has provided the scope of dependencies. Maven dependencies that use this scope are valid for compile and test classpath, but not at run time. The classic example is servlet-API, which is needed to compile and test a project, but doesn’t need to be introduced repeatedly by Maven when running the project because the container is already provided.
  • ** Runtime :** Runtime dependency scope. Maven dependencies that use this scope are valid for testing and running classpath, but not when compiling main code. A typical example is the JDBC driver implementation. Only the JDBC interface provided by the JDK is required to compile the main code of the project, and only the specific JDBC driver that implements the interface is required to perform tests or run the project.
  • **system:** Range of system dependencies. This dependency has exactly the same relationship with the three classpath and provided dependency scopes, but the path to the dependency file must be explicitly specified with the systemPath element when using system-scope dependencies. Because such dependencies are not resolved through Maven repositories and are often tied to native systems, they can constitute a non-portable build and should be used with caution. The systemPath element can refer to environment variables such as:
< the dependency > < groupId > javax.mail. SQL < / groupId > < artifactId > JDBC - stdext < / artifactId > < Version > 2.0 < / Version > <scope>system</scope> <systemPath>${java.home}/lib/rt.jar</systemPath> </dependency>Copy the code
  • **import:** Import dependency scope. Only in the dependencyManagement tag, importing the defined POM file has no real impact on the three classpath types.
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework</groupId> < artifactId > spring - framework - bom < / artifactId > < version > 4.3.16. RELEASE < / version > < type > pom < type > the < scope > import < / scope > </dependency> </dependencies> </dependencyManagement>Copy the code

What does this do? What???

The dependency scopes above, other than import, relate to the three classpath types as follows:

The dependencies are configured in POM.xml, but some dependencies are only valid for certain steps, and some dependencies are not valid for others.

Dependency mechanisms and features

Transitive dependence

For example, for an account-email project, account-email has a compile-scoped spring-code dependency, and spring-code has a compile-scoped commons-logging dependency. Commons-logging is a transitive dependency for account-email and will become a scope dependency for compile for account-email.

Assuming that A depends on B and B depends on C, we say that A is the first direct dependence on B,B is the second direct dependence on C, and A is A transitive dependence on C. The range of the first direct dependency and the second direct dependency determine the range of transitive dependencies, as shown in the figure below. The leftmost line represents the range of the first direct dependency, the top line represents the range of the second direct dependency, and the crossing cell in the middle represents the range of transitive dependencies.

From the picture above, we can find the following rule:

  • When the second direct dependency scope is compile, the transitive dependency scope is the same as the first direct dependency scope;
  • When the second direct dependency scope is test, the dependency is not passed;
  • If the second direct dependency is provided, the first direct dependency is also referred to as the dependency provided.
  • If the second direct dependency scope is Runtime, the transitive dependency scope is the same as the first direct dependency scope, but compile the runtime column.

Rely on mediation

When problems arise with transitive dependencies, it is clear from which dependency path the transitive dependency was introduced.

  1. Shortest path first
  • A – > B > C – > X (1.0)
  • A – > D – > X (2.0)
  1. The first person to declare priority
  • A – > B – > Y (1.0)
  • A – > C – > Y (2.0)

In this case, the first declarator takes precedence because the length of the dependent path is the same. If B dependencies are declared in the POM file before C dependencies, Y(1.0) is introduced, provided the path length is consistent.

But if you write this in the same file:

<dependencies> <dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> < version > 1.11 < / version > < / dependency > < the dependency > < groupId > Commons - codec < / groupId > The < artifactId > Commons - codec < / artifactId > < version > 1.10 < / version > < / dependency > < / dependencies >Copy the code

If you need to rely on Commons-codec and get 1.11 instead of 1.10, it can be understood that dependency mediation only occurs when the build is from a different POM and the build statement is in the same POM, so dependency mediation is not triggered.

Optional dependence

A depends on B, and B depends on X and Y. If all three of these fields are compile, then X and Y are transitive dependencies of A, but if I want X and Y not to be transitive dependencies of A, I can set the dependency to optional true.

The < project > < modelVersion > 4.0.0 < / modelVersion > < groupId > com. Juvenxu. Mvnbook < / groupId > < artifactId > project - b < / artifactId > < version > 1.0.0 < / version > < dependencies > < the dependency > < groupId > mysql < / groupId > < artifactId > mysql connector - Java < / artifactId > < version > 5.1.10 < / version > < optional > true < / optional > < / dependency > < the dependency > < groupId > postgresql < / groupId > < artifactId > postgresql < / groupId > < version > 8.4-701 jdbc3 < / version > <optional>true</optional> </dependency> </dependencies> </project>Copy the code

Eliminate dependence on

For example, spring-boot-starter-web has logback. I want to import log4j2, so I need to exclude the logback dependency package. Just import the desired package.

Note that only groupId and artifactId are required to declare EXCLustion, not the version element, because only groupId and artifactId are required to uniquely locate a dependency in the dependency diagram.

Depend on the sample

Usually, there are a series of projects under a common project. In this case, we can create a public dependencies POM file that contains all the public dependencies, which we call the POM parent of the other subproject POM. The following example will help you understand this concept better.

Following is a detailed description of the dependency diagram above:

  • App-ui-war depends on app-core-lib and app-data-lib.
  • Root is the parent project of app-core-lib and app-data-lib.
  • Root defines Lib1, lib2, and Lib3 as dependencies in its dependencies section.

The pom.xml file for app-uI-war looks like this:

The < 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 > com.com panyname. Groupname < / groupId > < artifactId > App UI - WAR < / artifactId > The < version > 1.0 < / version > < packaging > war < / packaging > < dependencies > < the dependency > < the groupId > com.com panyname. Groupname < / groupId > < artifactId > App - Core - lib < / artifactId > < version > 1.0 < / version > < / dependency > </dependencies> <dependencies> <dependency> <groupId>com.companyname.groupname</groupId> <artifactId> app-data-lib </artifactId> <version>1.0</version> </dependency> </dependencies> </dependencies> </project>Copy the code

The pom. XML file for app-core-lib looks like this:

The < 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" > < the parent > < artifactId > Root < / artifactId > < groupId > com.com panyname. Groupname < / groupId > < version > 1.0 < / version > < / parent > < modelVersion > 4.0.0 < / modelVersion > < groupId > com.com panyname. Groupname < / groupId > < artifactId > App - Core - lib < / artifactId > < version > 1.0 < / version > < packaging > jar < / packaging > < project >Copy the code

The pom. XML file for app-data-lib is as follows:

The < 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" > < the parent > < artifactId > Root < / artifactId > < groupId > com.com panyname. Groupname < / groupId > < version > 1.0 < / version > < / parent > < modelVersion > 4.0.0 < / modelVersion > < groupId > com.com panyname. Groupname < / groupId > < artifactId > App - Data - lib < / artifactId > < version > 1.0 < / version > < packaging > jar < / packaging > < project >Copy the code

Root’s pom.xml file looks like this:

The < 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 > com.com panyname. Groupname < / groupId > < artifactId > Root < / artifactId > The < version > 1.0 < / version > < packaging > pom < / packaging > < dependencies > < the dependency > < the groupId > com.com panyname. Groupname1 < / groupId > < artifactId > Lib1 < / artifactId > < version > 1.0 < / version > < / dependency > </dependencies> <dependencies> <dependency> <groupId>com.companyname.groupname2</groupId> <artifactId>Lib2</artifactId> <version>2.1</version> </dependency> </dependencies> <dependencies> < the groupId > com.com panyname. Groupname3 < / groupId > < artifactId > Lib3 < / artifactId > < version > 1.1 < / version > < / dependency > </dependencies> </project>Copy the code

Now when we build the app-UI-WAR project, Maven will find all the dependencies by walking through the dependency diagram and build the application.

  • Common dependencies can be grouped together using the concept of a POM parent. The dependencies for the app-data-lib and app-core-lib projects are listed in the Root project (see Root’s package type, which is a POM).
  • There is no need to declare Lib1, lib2, Lib3 as its dependencies in app-uI-w. Maven implements this detail by using a transitive dependency mechanism.

Afterword.

Began to think Mave things and a little more, learned a few days found that the original Mave is like this. This is the last article in the Mave series, and the next installment will be a look at the microservices framework. Hopefully, you’ll be able to cover the basics of Java as soon as possible.

B: well… Everyone is still working on the group, I’m the only one working overtime, actually. My heart has flown

Welcome everyone to like a lot, more articles, please pay attention to the wechat public number “Lou Zai advanced road”, point attention, do not get lost ~~