Maven入门知识

目录

前言

Maven快速入门

pom.xml编写

编写项目主代码

编写测试代码

打包和运行

Maven坐标和依赖

坐标详解

依赖的基本配置

Maven常用命令总结


前言

之前特地学习过Maven的相关基本知识点,但是当时并未动笔记录,导致很多基本标签的含义根本不记得又无处寻找,只能重新翻看书本,非常的繁琐,特对自己需要的东西做一些简单的入门总结。

Maven快速入门

pom.xml编写

首先创建一个maven工程,然后我们来看下maven的核心即pom.xml。POM(Project Object Model,项目对象模型)定义了项目的基本信息,用于描述项目如何被构建,声明项目依赖等等。如下是我刚新建的hello-world项目的pom.xml:

<?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">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.qingcha.maven</groupId>
    <artifactId>hello-world</artifactId>
    <version>1.0-SNAPSHOT</version>


</project>

代码的第一行是XML头,指定了该xml文档的版本和编码方式。紧接着是project元素,project是所有pom.xml的根元素,它还声明了一些pom相关的命名空间及xsd元素,虽然这些属性并不是必须的,但使用这些属性能够让第三方工具(如ide中的xml编辑器)帮助我们快速的编辑pom。

根元素下的第一个子元素modelVersion指定了当前pom模型的版本,对于Maven 2 及 Maven 3 来说,它只能是4.0.0。

最重要的是接下来的三个子元素groupId、artifactId、version,他们共同定义了一个项目基本的坐标,在Maven的世界里,任何的jar、pom或者war都是以基于这些基本的坐标来进行区分的。

groupId定义了项目属于哪个组,这个组往往和项目所在的组织或公司存在关联。譬如在googlecode上建立了一个名为myapp的项目,那么groupId就应该是com.googlecode.myapp。

artifactId定义了当前Maven项目在组中唯一的ID,组中不同的子项目(模块)往往分配不同的唯一标识。

version指定了当前项目当前的版本---1.0-SNAPSHOT,SNAPSHOT意为快照,说明该项目还处于开发中,是不稳定的版本。随着项目的发展,version会不断更新,如升级为1.0、1.1-SNAPSHOT、1.1等。

编写项目主代码

项目主代码和测试代码不同,主代码会被打包到最终的构件中(如jar),而测试代码只在测试运行时用到,不会被打包。默认情况下,Maven假设项目主代码位于 src/main/java 目录,只要遵循该约定,无需额外的配置,Maven会自动搜寻该目录找到项目主代码,因此我们需要遵循Maven的约定在该目录下创建我们自己的文件:com/qingcha/maven/helloworld/HelloWorld.java,其内容如下:

package com.qingcha.maven.helloworld;

public class HelloWorld {

    public String sayHello() {
        return "hello, world";
    }

    public static void main(String[] args) {
        System.out.println(new HelloWorld().sayHello());
    }
}

这里需要注意的一点是,项目中Java类的包都应该基于项目的groupId和artifactId,这样更加清晰,更加符合逻辑,也方便搜索构件或者Java类。

代码编写完毕后,使用maven进行编译,在项目根目录下运行命令:mvn clean compile,然后会看到控制台输出如下信息

[INFO] Scanning for projects...
[INFO] 
[INFO] -------------------< com.qingcha.maven:hello-world >--------------------
[INFO] Building hello-world 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ hello-world ---
[INFO] Deleting /project/Open-source/helloworld/target
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ hello-world ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ hello-world ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
[INFO] Compiling 1 source file to /project/Open-source/helloworld/target/classes
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.050 s
[INFO] Finished at: 2020-01-04T12:46:11+08:00
[INFO] ------------------------------------------------------------------------

从输出信息来看,maven主要做了三个操作:

  • clean任务:运用clean插件首先删除target/目录,默认情况下maven构建的所有输出都在target/目录中
  • resources任务:由于我们未定义项目资源,此处略过
  • compile任务:将项目主代码编译至target/classes目录下

编写测试代码

为了使项目结构清晰,项目主代码与测试代码应该分别位于独立的目录中,上文提到主代码的目录为 src/main/java ,对应的Maven

项目中默认的测试代码目录是 src/test/java ,因此我们也需要遵循约定在该目录下创建我们的测试类。当然为了测试首先还需要引入依赖,在java中一般都是使用junit项目,依赖如下

<?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">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.qingcha.maven</groupId>
    <artifactId>hello-world</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

上述xml中添加了dependencies元素,该元素下可以包含多个dependency元素以声明项目的依赖,这里添加了junit的依赖,由前面知识我们已经知道groupId、artifactId、version三者构成了一个Maven项目最基本的坐标,有了这段声明,Maven就能够自动下载junit-4.12.jar(从Maven的中央仓库进行下载)。

后面还有一个scope元素,scope为依赖范围,若依赖范围为test,则表示该依赖只对测试有效。换句话说,在测试代码中使用junit代码是没有问题的,但是在主代码中使用junit代码就会造成编译错误。如果不声明该元素,默认的范围为compile,表示该依赖对主代码和测试代码都有效。下文会详细介绍

接下来就是创建测试类了,在mac版idea下,将鼠标放至要创建测试类的类名下,然后按 option+enter 可快速创建测试类,测试类简单代码如下:

package com.qingcha.maven.helloworld;

import org.junit.Assert;
import org.junit.Test;

public class HelloWorldTest {

    @Test
    public void sayHello() {
        HelloWorld helloWorld = new HelloWorld();
        Assert.assertEquals("hello, world", helloWorld.sayHello());
    }
}

然后我们使用Maven执行测试,运行 mvn clean test ,输出信息如下:

[INFO] Scanning for projects...
[INFO] 
[INFO] -------------------< com.qingcha.maven:hello-world >--------------------
[INFO] Building hello-world 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ hello-world ---
[INFO] Deleting /project/Open-source/helloworld/target
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ hello-world ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ hello-world ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
[INFO] Compiling 1 source file to /project/Open-source/helloworld/target/classes
[INFO] 
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ hello-world ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /project/Open-source/helloworld/src/test/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ hello-world ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
[INFO] Compiling 1 source file to /project/Open-source/helloworld/target/test-classes
[INFO] 
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ hello-world ---
[INFO] Surefire report directory: /project/Open-source/helloworld/target/surefire-reports

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running com.qingcha.maven.helloworld.HelloWorldTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.069 sec

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.017 s
[INFO] Finished at: 2020-01-04T13:10:58+08:00
[INFO] ------------------------------------------------------------------------

由最下方的success表明我们的测试类执行通过了,分析上面的输出信息,Maven一共执行了六个任务:

  • clean任务:运用clean插件首先删除target/目录,默认情况下maven构建的所有输出都在target/目录中
  • resources任务:由于我们未定义项目资源,此处略过
  • compile任务:将项目主代码编译至target/classes目录下
  • testResources任务:资源未定义,略过
  • testCompile任务:测试代码编译
  • test任务:执行测试

打包和运行

将项目进行编译、测试之后,下一个重要步骤就是打包(package),当前新建的项目并没有指定打包类型,使用默认打包类型jar,简单的执行命令 mvn clean package 进行打包,可以看到输出

[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ hello-world ---
[INFO] Building jar: /project/Open-source/helloworld/target/hello-world-1.0-SNAPSHOT.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.010 s
[INFO] Finished at: 2020-01-04T13:41:38+08:00
[INFO] ------------------------------------------------------------------------

类似的,Maven会在打包之前执行编译、测试等操作,然后在最后执行jar任务即使用jar插件将项目主代码生成一个名为hello-world-1.0-SNAPSHOT.jar的包,该包同样位于/target 目录下。

至此,我们得到了项目的输出,如果有需要的话就可以复制这个jar文件到其他项目的classpath中从而使用HelloWorld类。但是如何才能让其他的Maven项目直接引用这个jar呢?还需要一个安装的步骤,执行 mvn clean install :

[INFO] --- maven-install-plugin:2.4:install (default-install) @ hello-world ---
[INFO] Installing /project/Open-source/helloworld/target/hello-world-1.0-SNAPSHOT.jar to /Users/xujia/Downloads/mydocument/maven/maven-repository/com/qingcha/maven/hello-world/1.0-SNAPSHOT/hello-world-1.0-SNAPSHOT.jar
[INFO] Installing /project/Open-source/helloworld/pom.xml to /Users/xujia/Downloads/mydocument/maven/maven-repository/com/qingcha/maven/hello-world/1.0-SNAPSHOT/hello-world-1.0-SNAPSHOT.pom
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.009 s
[INFO] Finished at: 2020-01-04T13:48:19+08:00
[INFO] ------------------------------------------------------------------------

同样的,执行安装命令时也会自动执行编译、测试、打包任务,从输出信息可以看到install任务将项目输出的jar包安装到了Maven本地仓库中,你可以打开相应的文件夹看到HelloWorld项目的pom和jar。之前讲述junit的pom及jar的下载的时候,我们说过只有构件被下载到本地仓库后,才能由所有Maven项目使用,这里是同样的道理,只有将HelloWorld的构件安装到本地仓库后,其他Maven项目才能使用它。

默认打包生成的jar包是不能够直接运行的,因为带有main方法的类信息不会添加到manifest中(打开jar文件中的META-INF/MANIFEST.MF文件,将无法看到Main-Class一行),为了生成可执行的jar文件,我们需要借助maven-shade-plugin插件(有多种方式均可以生成可执行的jar包,有兴趣可自行google),配置该插件如下:

<build>
                    <!-- 两种方式可以生成可执行jar包 -->
            <!--<plugin>-->
                <!--<groupId>org.apache.maven.plugins</groupId>-->
                <!--<artifactId>maven-shade-plugin</artifactId>-->
                <!--<version>3.2.1</version>-->
                <!--<executions>-->
                    <!--<execution>-->
                        <!--<phase>package</phase>-->
                        <!--<goals>-->
                            <!--<goal>shade</goal>-->
                        <!--</goals>-->
                        <!--<configuration>-->
                            <!--<transformers>-->
                                <!--<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">-->
                                    <!--<mainClass>com.qingcha.maven.helloworld.HelloWorld</mainClass>-->
                                <!--</transformer>-->
                            <!--</transformers>-->
                        <!--</configuration>-->
                    <!--</execution>-->
                <!--</executions>-->
            <!--</plugin>-->

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>2.6</version>
                <configuration>
                    <archive>
                        <manifest>
                            <mainClass>com.qingcha.maven.helloworld.HelloWorld</mainClass>
                        </manifest>
                    </archive>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
    </build>

此时再运行 mvn clean install ,在/target 目录下将会看到 hello-world-1.0-SNAPSHOT.jar 和 original-hello-world-1.0-SNAPSHOT.jar,前者是带有Main-Class信息的可运行jar,后者是原始的jar,打开 hello-world-1.0-SNAPSHOT.jar 的 META-INF/MANIFEST.MF,可以看到它其中便包含了Main-Class信息,便能确定程序的入口在哪里(解压jar包的命令为:jar xvf hello-world-1.0-SNAPSHOT.jar)

Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Built-By: xujia
Created-By: Apache Maven 3.5.4
Build-Jdk: 1.8.0_172
Main-Class: com.qingcha.maven.helloworld.HelloWorld

最后运行该jar包:java -jar hello-world-1.0-SNAPSHOT.jar,可以看到控制台输出hello, world,完美~

Maven坐标和依赖

坐标详解

Maven坐标为各种构件引入了秩序,任何一个构件都必须明确定义自己的坐标,而一组Maven坐标是通过一些元素定义的,它们是groupId、artifactId、version、packaging、classifier,先给出一组坐标定义,如下:

<groupId>org.sonatype.nexus</groupId>
<artifactId>nexus-indexer</artifactId>
<version>2.0.0</version>
<packaging>jar</packaging>

下面详细解释一下各个坐标元素

  • groupId:定义当前Maven项目隶属的实际项目。首先,Maven项目和实际项目不一定是一对一的关系。比如springframework这一实际项目,其对于的Maven项目会有很多,如spring-core、spring-context等,因此一个实际项目往往会被划分成很多个模块,就类似于我们上面说的组的概念。其次,groupId不应该对应项目隶属的组织或公司,因为一个组织下会有很多实际项目,如果groupId只定义到组织级别,那么artifactId只能定义Maven项目,那么实际项目这个层将难以定义。最后,groupId的表示方式与Java包名的表示方式类似,通常与域名反向一一对应。上例中,groupId 为 org.sonatype.nexus,org.sonatype表示Sonatype公司建立的一个非盈利性组织,nexus标识Nexus这一实际项目,该groupId与域名 nexus.sonatype.org 对应。
  • artifactId:该元素定义实际项目中的一个Maven项目(模块),推荐的做法是使用实际项目名称作为 artifactId 的前缀,如上例中的nexus-indexer。
  • version:该元素定义Maven项目当前所处的版本,如上例中 nexus-indexer 的版本是2.0.0。
  • packaging:该元素定义Maven项目的打包方式。首先,打包方式通常与所生成构件的文件扩展名对应,如上例中 packaging 为 jar,则最终的文件名为 nexus-indexer-2.0.0.jar。而使用war打包方式的Maven项目,最终生成的构件中会有一个 .war 文件,不过这不是绝对的。其次,打包方式会影响到构建的生命周期,比如jar打包和war打包会使用不同的命令。最后,当不定义 packaging 的时候,Maven会使用值 jar
  • classifier:该元素用来帮助定义构建输出的一些附属构件。附属构件与主构件对应,如上例中的主构件是 nexus-indexer-2.0.0.jar,该项目可能还会通过使用一些插件生成 nexus-indexer-2.0.0-javadoc.jar、 nexus-indexer-2.0.0-sources.jar 这样一些附属构件,其包含了java文档和源代码,这时候,javadoc和sources就是这两个附属构件的 classifier。这样,附属构件也就拥有了自己唯一的坐标。注意,不能直接定义项目的classifier,因为附属构件不是项目直接默认生成的,而是由附加的插件帮助生成。

上述5个元素中,groupId、artifactId、version是必须定义的,packaging 是可选的(默认为jar),classifier 是不能直接定义的。

依赖的基本配置

一个依赖声明可以包含如下的一些元素:

<project>
    ...
    <dependencies>
        <dependency>
            <groupId>...</groupId>
            <artifactId>...</artifactId>
            <version>...</version>
            <type>...</type>
            <scope>...</scope>
            <optional>...</optional>
            <exclusions>
                ...
                <exclusion>
                    ...
                </exclusion>
            </exclusions>
        </dependency>
        ...
    </dependencies>
    ...
</project>

根元素project下的 dependencies 可以包含一个或者多个 dependency 元素,以声明一个或多个项目依赖,每个依赖可以包含的元素有:

1、groupId、artifactId和version:依赖的基本坐标,对于任何一个依赖来说,基本坐标是最重要的,Maven根据坐标才能找到所需要的依赖

2、type:依赖的类型,对应于项目坐标定义的 packaging。大部分情况下,该元素不必声明,其默认值为jar。

3、scope:依赖的范围,比较常用的有三种:

  • compile:编译依赖范围,如果没有指定,就会默认使用该依赖范围。使用此依赖范围的Maven依赖,对于编译、测试、运行三种classpath都有效
  • test:测试依赖范围,只对于测试classpath有效,在编译主代码或者运行项目的时候无法使用此类依赖
  • provided:已提供依赖范围,使用此依赖范围的Maven依赖,对于编译和测试classpath有效,但在运行时无效。典型的例子是servlet-api,编译和测试项目的时候需要该依赖,但在运行项目的时候,由于容器已经提供,就不需要Maven重复的引入一遍
  • runtime:运行时依赖范围,对于测试和运行时有效,编译时无效

4、optional:标记依赖是否可选,为true的时候表示是可选依赖,如果项目A依赖于项目B,项目B依赖于项目X和Y,B对于X和Y的依赖都是可选依赖,那么X和Y将对A没有任何影响,即不会进行依赖传递。

5、exclusions:用来排除传递性依赖,需要注意的是,声明exclusion的时候只需要groupId和artifactId,而不需要version元素,这是因为只需要groupId和artifactId就能唯一定位依赖图中的某个依赖。

依赖这一块东西很多,对于一个好的项目来说,管理好多个项目之间的依赖也很重要,入门篇就先记录这么点把。

 

Maven常用命令总结

  • mvn clean compile:删除/target目录并编译项目主代码
  • mvn clean test:执行全部测试代码
  • mvn clean package -D maven.test.skip=true:打包,但是不执行测试用例,也不编译测试用例
  • mvn clean package -U -D skipTests:打包并更新快照,不执行测试用例,但编译测试用例类生成相应的class文件至target/test-classes下。
  • mvn clean install:将打完的包部署到本地maven仓库,以供其他项目使用
  • mvn clean deploy:将打完的包部署到本地maven仓库和远程maven私服仓库
  • mvn dependency:tree:查看依赖树
  • mvn dependency:tree -D output=*.txt:查看依赖树并将树输出到文本中

猜你喜欢

转载自blog.csdn.net/m0_38001814/article/details/103831721