This is the 9th day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021

reading

Take a fresh look at Maven as a tool

The Maven project goes from 0 to 1

preface

Dependency management is a core feature of Maven, and it is the one we use most. For developers, it may not be necessary to package deployment with Maven, but it is difficult to avoid adding or removing dependencies.

Before Maven or a similar build tool, managing the dependencies of a single project was relatively simple, but managing the dependencies of multi-module projects and applications made up of hundreds of modules was quite complex, and Maven came along in part to solve this problem.

Depend on the transfer

If your project depends on a dependency, such as ABC, which itself depends on XYZ, then your project also depends on XYZ. This feature is called dependency passing.

There is no limit to how many levels of dependency can be passed, and it is only in the case of circular dependencies that problems arise.

Cyclic dependencies are those in which A depends on B, and B depends on C. If A is added to project C, A cyclic dependency is formed.

Maven has special mechanisms to address the problems that arise because of the dependency passing feature.

Dependency mediation: Dependency mediation is the mechanism of choice when there are multiple versions of dependencies or transitive dependencies under a project. Maven uses the “nearest rule” for selection, as in the following case:

A │ ├── B │ ├── C │ ├─ D │ ├─Copy the code

There are two dependency lines A->B->C->D2.0 and A->E->D1.0 in project A, so D1.0 will be used as A dependency in project A construction, because D1.0 is closer to project A than D2.0.

If two dependencies are the same distance apart, the dependency defined earlier is used first.

It is also possible to display the version of the specified dependency in the A project, such as the D2.0 version used in the following example:

A ├ ─ ─ B │ └ ─ ─ C │ └ ─ ─ D 2.0 └ ─ ─ E │ └ ─ ─ D 1.0 └ ─ ─ D 2.0Copy the code

DependencyManagement: You can specify the version of a dependency in DependencyManagement. For a specified dependency, the version will be used directly when a transfer dependency is encountered. In the previous example we added D2.0 directly to A dependency. We can also specify the version of the D dependency directly in DependencyManagement.

Dependency Scope: You can specify in which build phase a dependency will be used, described separately later.

Dependency Exclusion: If Project X is dependent on project Y and project Y is dependent on Project Z, then project X can explicitly exclude project Z using the Exclusion element.

Optional dependencies: If item Y depends on item Z, item Y can mark item Z as an optional dependency using the optional element. When item X depends on item Y, X will depend only on Y and not on the optional dependency Z of Y. Project X can add dependencies on Z as it chooses.

Although Maven has rely on transmission characteristics, but we should specify the dependencies we need version, such as when our project A relies on B, B is dependent on the C, if we are not in A specified directly C version, when B to C version changes, are likely to cause A build our project failure.

Depends on scope scope

The dependency scope is used to limit the transitivity of dependencies and determine at what stage a dependency takes effect.

There are six scope options in Maven:

compile

This is the default scope. Representation is available at all phases, and dependencies are propagated to dependent projects.

provided

You want the JDK or container to provide dependencies at run time. For example, when JavaEE builds a Web application, the dependency scope of the Servlet API and related JavaEE APIS is set to Provided because these classes are already provided in the Web container. Dependencies with this scope are added to the classpath for compilation and testing, not the runtime classpath. It’s not transitive.

runtime

This dependency is not required for compilation, but is required at runtime. Maven contains dependencies for this scope at run time and in the test classpath.

test

This scope indicates that the dependency is not required for proper use of the application and is only available during the test compilation and execution phases. This range is not transitive. Typically, this scope is used for test libraries such as JUnit and Mockito. That is, dependencies that are not used in SRC /main/ Java can use this scope if they are only used in SRC /test/ Java.

system

This scope is similar to the provided scope, except that the JAR files are supplied directly and are not searched in the repository at build time.

import

This scope differs from the others in that it is only supported for a

node whose type node is POM.


      
<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.heiz</groupId>
    <artifactId>hello-world-scope</artifactId>
    <packaging>pom</packaging>
    
	<dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.heiz</groupId>
                <artifactId>hello-world-package</artifactId>
                <version>1.0.0</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.google.guava</groupId>
                <artifactId>guava</artifactId>
                <version>18.0</version>
            </dependency>
        </dependencies>
    <dependencyManagement>
</project>
Copy the code

The implication of this configuration is that, in the current project, the dependencies from the Hello-world-package project are introduced into the current project, similar to an inclusion import of configuration information.

DependencyManagement

The role of DependencyManagement can be summarized as follows:

  • Unified management of common dependencies simplifies subproject configuration
  • Control transitive dependency version selection for dependent items

Here are two examples of these effects.

Example: Simplify subproject configuration

Dependencies in project A

<project>.<dependencies>
    <dependency>
      <groupId>group-a</groupId>
      <artifactId>artifact-b</artifactId>
      <version>1.0</version>
      <type>bar</type>
      <scope>runtime</scope>
    </dependency>
	
    <dependency>
      <groupId>groupId-a</groupId>
      <artifactId>artifact-a</artifactId>
      <version>1.0</version>
      <exclusions>
        <exclusion>
          <groupId>group-c</groupId>
          <artifactId>artifact-c</artifactId>
        </exclusion>
      </exclusions>
    </dependency>	
  </dependencies>
</project>

Copy the code

Dependencies in project B

<project>.<dependencies>
    <dependency>
      <groupId>group-a</groupId>
      <artifactId>artifact-b</artifactId>
      <version>1.0</version>
      <type>bar</type>
      <scope>runtime</scope>
    </dependency>

    <dependency>
      <groupId>groupId-a</groupId>
      <artifactId>artifact-c</artifactId>
      <version>1.0</version>
    </dependency>	
  </dependencies>
</project>
Copy the code

Project A and project B have A common dependency, and each has A unique dependency. This information can be extracted into the DependencyManagement in the parent POM:

<project>.<dependencyManagement>
    <dependencies>
		<dependency>
		  <groupId>groupId-a</groupId>
		  <artifactId>artifact-a</artifactId>
		  <version>1.0</version>
		  <exclusions>
			<exclusion>
			  <groupId>group-c</groupId>
			  <artifactId>artifact-c</artifactId>
			</exclusion>
		  </exclusions>
		</dependency>
		
		<dependency>
		  <groupId>group-a</groupId>
		  <artifactId>artifact-b</artifactId>
		  <version>1.0</version>
		  <type>bar</type>
		  <scope>runtime</scope>
		</dependency>
		
		<dependency>
		  <groupId>groupId-a</groupId>
		  <artifactId>artifact-c</artifactId>
		  <version>1.0</version>
		</dependency>
    </dependencies>
  </dependencyManagement>
</project>
Copy the code

Then the POM in the A and B subprojects becomes very simple:

The project a.

<project>.<dependencies>
    <dependency>
      <groupId>group-a</groupId>
      <artifactId>artifact-b</artifactId>
      <type>bar</type>
    </dependency>
    <dependency>
      <groupId>groupId-a</groupId>
      <artifactId>artifact-a</artifactId>
    </dependency>	
  </dependencies>
</project>
Copy the code

Project B

<project>.<dependencies>
    <dependency>
      <groupId>group-a</groupId>
      <artifactId>artifact-b</artifactId>
      <type>bar</type>
    </dependency>

    <dependency>
      <groupId>groupId-a</groupId>
      <artifactId>artifact-c</artifactId>
    </dependency>	
  </dependencies>
</project>
Copy the code

In the component dependence and matching is through DependencyManagement {groupId, artifactId, type, classifier}.

GroupId, artifactId, class class, class class, class class, class class, class class, class class, class class, class class, class class, class class, class class, class class, class class, class class, class class, class class, class class So it can also be null if the dependency type is JAR.

Example: Delivering dependent version control

The project a.

<project>
 <modelVersion>4.0.0</modelVersion>
 <groupId>maven</groupId>
 <artifactId>A</artifactId>
 <packaging>pom</packaging>
 <name>A</name>
 <version>1.0</version>
 <dependencyManagement>
   <dependencies>
     <dependency>
       <groupId>test</groupId>
       <artifactId>a</artifactId>
       <version>1.2</version>
     </dependency>
     <dependency>
       <groupId>test</groupId>
       <artifactId>b</artifactId>
       <version>1.0</version>
       <scope>compile</scope>
     </dependency>
     <dependency>
       <groupId>test</groupId>
       <artifactId>c</artifactId>
       <version>1.0</version>
       <scope>compile</scope>
     </dependency>
     <dependency>
       <groupId>test</groupId>
       <artifactId>d</artifactId>
       <version>1.2</version>
     </dependency>
   </dependencies>
 </dependencyManagement>
</project>
Copy the code

Project B

<project>
  <parent>
    <artifactId>A</artifactId>
    <groupId>maven</groupId>
    <version>1.0</version>
  </parent>
  <modelVersion>4.0.0</modelVersion>
  <groupId>maven</groupId>
  <artifactId>B</artifactId>
  <packaging>pom</packaging>
  <name>B</name>
  <version>1.0</version>
 
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>test</groupId>
        <artifactId>d</artifactId>
        <version>1.0</version>
      </dependency>
    </dependencies>
  </dependencyManagement>
 
  <dependencies>
    <dependency>
      <groupId>test</groupId>
      <artifactId>a</artifactId>
      <version>1.0</version>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>test</groupId>
      <artifactId>c</artifactId>
      <scope>runtime</scope>
    </dependency>
  </dependencies>
</project>
Copy the code

When Maven runs on project B, version 1.0 of artifactId A, B, C, and D will be used, regardless of the specific version specified in their POM.

  • A and C are in project Bdependencies, so version 1.0 will be used in accordance with the nearest principle.
  • Because project B’s parent, project A’sdependencyManagementIf b is a dependency of A or C (or a passing dependency), version 1.0 is also used. Scope iscompileBecause,DependencyManagement has a priority over the proximity principle.
  • And d is defined in project BdependencyManagementIf D is a dependency of A or C (or a passing dependency), version 1.0 will be selected.

BOM

BOM is short for Bill Of Materials. It is translated into Bill Of Materials. I didn’t understand it very well at first.

In plain English, a BOM is a special POM file that is used to manage the versions of items in a project and can be customized.

A project’s root POM file is typically defined as a BOM, along with some version numbers that may need to be relied upon in subprojects.

Here is a BOM POM file.

<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.heiz</groupId>
  <artifactId>bom</artifactId>
  <version>1.0.0</version>
  <! -- Packaging -- poM -->
  <packaging>pom</packaging>
  
    <! Define the project version number to be used in the submodule.
  <properties>
	<spring.version>4.3.4. RELEASE</spring.version>
    <spring.boot.version>1.4.2. RELEASE</spring.boot.version>
  </properties>
 
  <dependencyManagement>
    <dependencies>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-beans</artifactId>
			<version>${spring.version}</version>
		</dependency>
    </dependencies>
  </dependencyManagement> 
 <! -- Submodule artifactId -->
  <modules>
    <module>bom-child</module>
  </modules>
</project>
Copy the code

The BOM file defines spring.version and Spring.boot. version. Then ${spring.version} can be used to obtain the version defined in the BOM in the PoM file of the dependencyManagement or subproject.

Using an existing BOM file in a project can be done both by inheritance and import.

summary

This installment focuses on some of the management styles and features Maven relies on. DependencyManagement specifies the version of a dependencyManagement dependencyManagement. The version of a dependencyManagement is dependencyManagement. And special POM document BOM.


That’s all for this episode. If it helps, a “like” is my biggest encouragement.