Quickly understand Maven's core concepts and engineering practices

Posted by ORiGIN on Tue, 25 Jan 2022 05:34:00 +0100

introduce

Maven is a project management and build tool.

Do you still don't know what Maven does? Project management and construction sound rather empty,

For example, our alpha e-commerce project is divided into order, commodity, merchant, user and marketing modules. The order module needs the code of the user module, such as the interface for querying user information. Does it need the students of the user module to copy the code to the order module? There will be many problems. First, the code written by the students of the user module may have bug s and need to be repaired. In addition, the functions of the module have been iterative. For example, in the original interface, only query by userId is added, and query by name and mobile phone number are added. After writing, the code needs to be copied again. The efficiency is very low. Smart engineers think of a method, These codes are centrally stored and managed in the form of package + version. This centralized storage place is called the warehouse. Everyone is subject to the warehouse. Changes are maintained through the version number. In order to prevent every small change, the version needs to be modified. It also distinguishes the offline test version from the online production environment, which is divided into Snapshot package and Release package.

In general, Maven can easily manage the project structure and dependencies.

Warehouse

In Maven, a very important concept is Repository.

image-20220123161718538

maven warehouse is divided into local warehouse and remote warehouse.

Remote warehouse also includes private server and central warehouse. Private server is easy to understand. Many companies will build their own private warehouse address in the LAN.

Here's a concept to distinguish. Many people confuse private server with mirror. Mirror is a replica of the central warehouse. The reason is very simple. The central warehouse is abroad, and your speed of accessing the central warehouse is slow. The mirror server is in China, which is equivalent to a cache.

If you download maven, you can view the configuration of Maven warehouse in Maven configuration file. The figure below shows the Maven configuration on Angela's computer, which is generally placed in the user directory m2 folder, setting xml.

image-20220123161949266

You can see that the repository is the local repository folder, setting XML is a maven configuration.

Let's turn on settings Take a look.

<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
  
  <localRepository>/${user.home}/.m2/repository</localRepository>
  <interactiveMode>true</interactiveMode>
  
  <!--Some settings of the server. Some settings such as user name and password should not be associated with the project pom.xml Put it together. Authentication information such as password should exist in settings.xml File. -->
  <servers>
    <server>
      <!--This is server of id(Note that the user is not logged in id),Should id And distributionManagement in repository Elemental id Match. -->
      <id>server001</id>
      <!--Authentication user name-->
      <username>my_login</username>
      <!--Authentication password-->
      <password>my_password</password>
      <!--The location of the private key used in authentication -->
      <privateKey>${usr.home}/.ssh/id_dsa</privateKey>
      <!--The private key password used for authentication. -->
      <passphrase>some_passphrase</passphrase>
    </server>
  </servers>
 <mirrors>
   <mirror>
        <!-- Unique identifier of the mirror-->
        <id>aliyunmaven</id>
        <!-- Of the mirrored server id. -->
        <mirrorOf>central</mirrorOf>
        <name>Alibaba cloud public warehouse</name>
        <url>https://maven.aliyun.com/repository/public</url>
    </mirror>
  </mirrors>
  <profiles>
    <profile>
     <!-- profile Unique identification of -->
     <id>aliyun</id>
     <!-- Let current profile Effective condition (activate current) profile) -->
      <activation>
        <!-- The default activation status is false -->
        <activeByDefault>false</activeByDefault>
         <!-- jdk Is 1.8 Activate when -->
        <jdk>1.8</jdk>
      </activation>
     <!-- Remote warehouse list -->
     <repositories>
      <repository>
        <id>aliyun</id>
        <name>aliyun</name>
        <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
        <layout>default</layout>
      </repository>
     </repositories>
   </profile>
  </profiles>
  <activeProfiles>
    <activeProfile>aliyun</activeProfile>
  </activeProfiles>
</settings>

We explain one by one:

  • Local repository: the warehouse address where the maven package pulled from the remote warehouse is stored locally;

  • Interactive mode: indicates whether maven needs to interact with the user to get input. We know the function of this parameter by using mvn archetype:generate on the command line. You will be prompted to enter groupId and artificialId, and whether you agree to generate the project. If maven needs to interact with the user to get input, it should be set to true, otherwise it should be false. The default is true.

  • servers: access to some warehouses requires user name and password authentication, such as the company's own warehouse. Generally, repositories and distribution management will be in the pom of the project XML definition, but the user name and password are not suitable to be directly put in plaintext in the project pom, and some passwords are private to everyone, so they are more suitable in Maven settings XML configuration;

  • mirrors: the word mirror means image. Here is to configure the image of the remote warehouse. Angela mentioned earlier that the central warehouse may have large and slow access, so you can use the image to cache the pressure of the remote warehouse.

    The mirror I configured above is the image of domestic Alibaba cloud to the central warehouse. The value of mirrorOf is central, which means the image of the central warehouse;

  • profiles: the list of build configurations adjusted by environment parameters.

    settings. The profile element in XML is POM A cropped version of the profile element in XML. It contains only five parts

    • id: unique identification of the profile

    • activation: condition to make the current profile effective (activate the current profile)

    • repositories: if the current profile is active, the list of remote warehouses defined in it will be used as the remote warehouses of the current pom, and maven will use this group of remote warehouses to build the project; You can see here that Alibaba cloud's remote warehouse is used.

    • Plugin repositories: list of remote repositories of plug-ins; There are two types of warehouses in Maven, one is the warehouse for storing artifacts, and the other is the warehouse for storing plugin plug-ins. Here you can specify the repository of the plugin.

    • properties: after the profile takes effect, the effective property values are stored in maven context in the form of key value pairs. These values can be displayed in POM Use the tag ${X} anywhere in XML.

      For example, there are five ways to get the property value of maven context.

      <!--
        1. env.X: Add before a variable"env."A prefix is returned shell Environment variables. for example,"env.PATH"Refers to $path Environment variables (in Windows It is%PATH%).
        2. project.x: Refers to POM The corresponding element value in. for example: <project><version>1.0</version></project>adopt ${project.version}get version Value of.
        3. settings.x: Refers to settings.xml The value of the corresponding element in the. For example:<settings><offline>false</offline></settings>adopt ${settings.offline}get offline Value of.
        4. Java System Properties: All available java.lang.System.getProperties()The accessed properties can be POM Use this form of access in, for example ${java.home}.
        5. x: stay<properties/>Element, or in an external file, to ${someVar}Use in the form of.
       -->
      <properties>
          <user.install>${user.home}/our-project</user.install>
      </properties>

      As shown in the above figure, you can get the attribute value through ${user.install} in pom.

      We often use this function in maven configuration of engineering projects. We manage all package versions in the pom file of the parent maven package and reference the version value through ${XX.version}, which will be discussed later.

  • activeProfiles: a list of manually activated profiles. For example, the above configuration is to activate aliyun's profile.

If we follow the above settings XML file configuration. When we package, how does maven find the package?

maven will first find the package from the local warehouse, that is, your own computer. If not, it will query from the warehouse of the active profile. If not, it will go to the central warehouse. If you configure the mirror of the central warehouse, it will be intercepted at this time and directly find the package from the mirrored warehouse. This is the process of maven automatically pulling packages.

Summary: warehouses are divided into local warehouses and remote warehouses. Local warehouses are equivalent to package caches pulled on demand. Remote warehouses are divided into central warehouses and private servers. Private servers are generally LAN access warehouses built by companies, schools or other groups to accelerate package access. In addition, some large IT manufacturers, such as Alibaba cloud, will build images of central warehouses, If we configure the image of the central warehouse, all requests to access the central warehouse will be redirected to the image warehouse. The customization of maven warehouse is in settings XML file (divided into system directory and user directory. It is generally recommended to use user directory). profile can set different activation conditions and configure the corresponding warehouse.

The maven in the project is described below.

engineering structure

Maven defines a standard directory structure based on the principle that convention is greater than configuration. As follows:

catalogueobjective
${basedir}Store POM XML and all subdirectories
${basedir}/src/main/javajava source code of the project
${basedir}/src/main/resourcesResources of the project, such as the property file, spring MVC xml
${basedir}/src/test/javaTest classes of the project, such as Junit code
${basedir}/src/test/resourcesResources for testing
${basedir}/src/main/webapp/WEB-INFWeb application file directory, web project information, such as storing web XML, local image, jsp view page
${basedir}/targetPackage output directory
${basedir}/target/classesCompile output directory
${basedir}/target/test-classesTest compilation output directory
Test.javaMaven will only automatically run test classes that conform to this naming convention
~/.m2/repositoryMaven default local warehouse directory location

Our standard engineering structure is shown in the figure below:

alpha-mall
├── pom.xml
├── src
│   ├── main
│   │   ├── java
│   │   └── resources
│   └── test
│       ├── java
│       └── resources
└── target

Alpha mall is the ${basedir} directory. The rest of the comparison table is very clear.

Remember the properties property we mentioned earlier? basedir is the default properties in maven project.

Maven POM

POM (project object model) is the basic work unit of Maven project. It is an XML file that contains the basic information of the project, which is used to describe how to build the project, declare project dependencies, and so on.

Before creating POM, we first need to describe the project group (groupId) and the unique ID of the project.

<?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">
    <!-- Model version --> 
    <modelVersion>4.0.0</modelVersion>
    <!-- The unique flag of the company or organization, and the path generated during configuration is also generated, as com.angela.alpha.alpha-shoppingmall,maven The project will be marked as jar Package local path:/com/angela/alpha/alpha-shoppingmall -->
    <groupId>com.angela.alpha</groupId>
    <!-- Unique name of the project ID,One groupId There may be several projects below, which rely on artifactId To distinguish -->
    <artifactId>alpha-shoppingmall</artifactId>
    <!-- Version number -->
    <version>1.0-SNAPSHOT</version>

</project>

All POM files require a project element and three required fields: groupId, artifactId, version.

nodedescribe
projectThe root label of the project.
modelVersionThe model version needs to be set to 4.0.
groupIdThis is the identification of the engineering team. It is usually unique in an organization or project. For example: groupid of ant sofa project: com alipay. sofa
artifactIdThis is the logo of the project. It is usually the name of the project. For example, consumer banking. groupId and artifact ID together define the location of the artifact in the warehouse. Artifact ID of ant sofa project: sofabot dependencies
versionThis is the version number of the project. In the repository of artifact, it is used to distinguish different versions. For example: com alipay. sofa: sofaboot-dependencies:1.0 com. alipay. sofa: sofaboot-dependencies:1.1

Dependency management:

image-20220123175622774

A depends on B, B depends on C, and a also depends on C.

For example, chestnuts:

image-20220123175853833

Build lifecycle

image-20220123194248631

I cut a picture of IDEA Maven Lifecycle

  • clean up: delete the compiled old class bytecode file;
  • Compile: compile the java source program into a class bytecode file and compile the resource resource file;
  • Test: automatically run test cases
  • report: results of test program execution
  • Package: War package for dynamic Web project and jar package for java project
  • Install: copy the packaged files to the specified location in the "warehouse". For example, when we build multiple modules, we use install to install the package to the local warehouse;
  • Deploy: deploy the package to the specified warehouse, or configure the running directory of application deployment;

These lifecycles are actually completed in the form of plug-ins. In fact, maven's major functions are completed through plug-ins.

engineering practice

Now let's practice a project. As I introduced in the previous article, now generally larger projects adopt multi module management. If the domain concept is introduced, there are generally layers and domain layers, how should we set the maven package structure for such a project. For example:

image-20220123200043364

First, we will define these modules contained in the parent project

    <modules>
        <module>alpha-mall-api</module>
        <module>alpha-mall-dao</module>
        <module>alpha-mall-manager</module>
        <module>alpha-mall-service</module>
        <module>alpha-mall-web</module>
        <module>start</module>
    </modules>

Then comes dependency management, which is very important in multi module projects.

The configuration in dependency management is not actually introduced, but only for version management.

The version of the dependency package used in the whole project should be managed in the dependency management of the parent project pom, so that the version will not be disordered and the management will be centralized.

    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.4.1</spring-boot.version>
        <arthas-spring-boot.version>3.4.8</arthas-spring-boot.version>
    </properties>

  <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.taobao.arthas</groupId>
                <artifactId>arthas-spring-boot-starter</artifactId>
                <version>${arthas-spring-boot.version}</version>
                <type>jar</type>
            </dependency>
        </dependencies>
    </dependencyManagement>

Generally, you can define the versions of third-party packages directly in properties, such as Spring boot and Spring cloud.

In addition, we have noticed that the spring boot dependencies package uses pom, which means that we do not really need to introduce spring boot dependencies. The jar contained in this package basically covers the spring family bucket. Spring and other matching third-party middleware have been. pom means that we only need maven package version information. What are the benefits of this, For example, when you introduce a third-party package, you are worried that the version you use is incompatible with the version of spring. Don't worry, spring has long thought for you. When you introduce spring boot dependencies, it basically covers the commonly used components in the market, and the relative versions are also defined for you. Take a look at the screenshot:

image-20220123202210031

Another attribute is scope, which is also very important. Let's look at the optional values of scope:

scope valueValid range (compile, runtime, test)Dependency deliveryexample
compileallyesspring-core
providedcompile, testnoservlet-api
runtimeruntime, testyesJDBC Driver
testtestnoJUnit
systemcompile, testyes

Important: compile attribute is not configured by default.

compile: the default value. compile means that the dependent project needs to participate in the compilation of the current project. Of course, subsequent tests and run cycles are also involved. It is a strong dependency. Packaging usually needs to be included.

Provided: valid when compiling and testing, but invalid when running. For example: servlet API, when running the project, Tomcat has been provided as a running container, so Maven doesn't need to introduce it again and again.

runtime: valid when running and testing, but invalid when compiling code. For example: jdbc driver implementation, only the JDBC interface provided by the JDK is required for project code compilation, and the specific jdbc driver of the above interface is required only when testing or running the project.

test: only valid when testing, for example: JUnit.

System: valid when compiling and testing, but invalid when running. The difference between and provided is that when using system wide dependencies, the path of the dependent file must be explicitly specified through the systemPath element. Since such dependencies are not resolved through Maven warehouse, and are often bound to native systems, which may make the build non portable, they should be used with caution.

Dependency passing of scope A – > b – > C. The current project is A, A depends on B, and B depends on C. If you know the scope of B in project A, how do you know the scope of C in project A? The answer is: when C is test or provided, C is directly discarded, and A does not depend on C; Otherwise, A depends on C, and C's scope inherits B's scope.

OK, finally, let's select the maven package of a sub module. The configuration is as follows:

In the start module, let's look at the following:

    //Define the parent module. The relativePath # relative path indicates that the parent module is in the upper layer
  <parent>
        <groupId>com.angela.alpha</groupId>
        <artifactId>alpha-mall</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>

   <dependency>
      <groupId>org.mybatis.spring.boot</groupId>
      <artifactId>mybatis-spring-boot-starter</artifactId>
  </dependency>

    <dependency>
      <groupId>com.taobao.arthas</groupId>
      <artifactId>arthas-spring-boot-starter</artifactId>
      <scope>runtime</scope>
    </dependency>

You can see that package dependencies do not have version numbers, because all version numbers are uniformly defined by the parent module, which can also reduce the inconsistency of package versions and the existence of multiple versions of a project.

  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    <exclusions>
      <exclusion>
        <groupId>org.junit.vintage</groupId>
        <artifactId>junit-vintage-engine</artifactId>
      </exclusion>
    </exclusions>
  </dependency>

In addition, we often see exclusion, which is very effective in solving package conflicts.

Let's first talk about the causes of package conflicts, such as

  • A - > b - > C, a depends on B, and B depends on C
  • A - > D - > C, a depends on D, D depends on C

However, the versions of C introduced by B and D are inconsistent, and errors will be reported during compilation. At this time, it is necessary to determine which version of the error reporting code is used. The general principle is to use the new version, because most of the time, the third-party package upgrade will consider compatibility and will not directly overwrite the original version. However, there are special cases, which need to be analyzed in a specific case.

We can analyze conflicting jar packages with the help of Dependency Analyzer in Maven Helper plug-in.

Due to space reasons, the introduction to Maven is here first. I hope you can get something after reading it.

Topics: Java Maven