简单使用 maven

Posted by Neal Wang on April 6, 2017

运行

mvn [options] [<goal(s)>] [<phase(s)>]

可以使用 -h 查看帮助

mvn -h

典型的使用Maven生命周期来构建一个项目的调用

mvn package

生命周期的构建过程为:

  • clean - pre-clean, clean, post-clean
  • default - validate, initialize, generate-sources, process-sources, generate-resources, process-resources, compile, process-classes, generate-test-sources, process-test-sources, generate-test-resources, process-test-resources, test-compile, process-test-classes, test, prepare-package, package, pre-integration-test, integration-test, post-integration-test, verify, install, deploy
  • site - pre-site, site, post-site, site-deploy

重新构建项目并且打包输出,生成文档并发布到仓库管理:

mvn clean deploy site-deploy

只是打包并保存到本地供其它项目使用:

mvn clean install

在有些时候,你想在项目之外调用一个 Maven 执行的特殊任务,即插件的目标:

mvn archetype:generate

或者

mvn checkstyle:check

Maven 生命周期的阶段

  • validate:验证项目是否正确,所有必要信息是否可以获得
  • compile:编译项目的源码
  • test:使用合适的单元测试框架测试编译后的源码,这些测试需要代码被编译或部署
  • package:把编译后的代码打包成制定形式,如JAR,WAR
  • integration-test:如果必要的话,在集成测试可以运行的环境下,运行并部署 package
  • verify:运行检查 package 是否可用并符合质量标准
  • install:把 package 安装到本地,作为其他本地项目的依赖
  • deploy:在集成环境或者正式环境中使用,把最终的 package 复制到远端仓库共享给其他开发者和项目

还有两个不在 default 阶段的生命周期:

  • clean:清除上一次构建的结果
  • site:生成该项目的站点文件

阶段和目标可以按顺序执行:

mvn clean dependency:copy-dependencies package

这代表项目会依次执行清理项目,复制依赖,打包项目

创建第一个项目

可以使用 Maven 提供的原型机制创建你的第一个 Maven 项目,可以使用下面的命令在命令行中创建一个最简单的 Maven 项目:

mvn -B archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DgroupId=com.mycompany.app -DartifactId=my-app

生成的 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/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.mycompany.app</groupId>
    <artifactId>my-app</artifactId>
    <packaging>jar</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>my-app</name>
    <url>http://maven.apache.org</url>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

生成的目录结构

my-app
|-- pom.xml
`-- src
    |-- main
    |   `-- java
    |       `-- com
    |           `-- mycompany
    |               `-- app
    |                   `-- App.java
    `-- test
        `-- java
            `-- com
                `-- mycompany
                    `-- app
                        `-- AppTest.java

这是标准的 Maven 项目的结构,应用的资源放在 ${basedir}/src/main/java 下面,测试的资源放在 ${basedir}/src/main/java 下面,${basedir} 代表包含 pom.xml 的目录,

编译应用源码

前往 pom.xml 所在的文件目录下,在命令行中执行:

mvn compile

如果你是第一次执行这个命令,Maven 需要去下载它需要的插件和依赖,在新安装的 Maven 中,会需要一点时间(在上面这个例子中,大概四分钟),如果你再次执行这个命令,Maven 如果已经有了它需要的,就不会再下载任何东西

你可以看到,编译后的类被放在 ${basedir}/target/classes 目录下,这是另一个 Maven 的约定,你会发现,你使用 Maven 约定的话会省去不少事,不用去通知 Maven 你的资源在哪里,你的输出路径是哪里,你可以通过很小的动作来做很多事情

编译测试源码,执行单元测试

执行下面的代码即可:

mvn test

需要注意的是:

  • 如果你是第一次执行这个命令,你会发现 Maven 这次下载了更多的依赖,这些依赖和插件都是它执行测试要用的,编译用的插件它已经有了,所以不会再去下载
  • 在执行编译测试前,Maven 会先编译主要代码。

如果你只是想编译你的测试代码,而不想执行:

mvn test-compile

创建一个 JAR 包并装载到本地仓库

你可以使用下面的命令直接创建一个 JAR 包:

mvn package

如果你去看一眼你的POM文件,你会发现元素 packaging 的值为 jar,Maven 会按照这个值生成对应文件类型。

生成的 jar 文件会放在 ${basedir}/target 目录下

使用下面的命令把你生成的 jar 文件装载到你的本地仓库(默认是 ${user.home}/.m2/repository):

mvn install

你会发现 surefire 这个插件(执行测试的)会寻找文件里用特殊命名规则命名的测试,默认的有:

  • **/*Test.java
  • **/Test*.java
  • **/*TestCase.java

默认不包含:

  • **/Abstract*Test.java
  • **/Abstract*TestCase.java

生成项目网站

现在来看一个 Maven 高度重视的特点:你不用做额外的工作就可以生成一个介绍你的项目基本信息的网页。里面会包含你的项目在 POM 中提到的基本信息。

mvn site

还有许多可以独立执行的目标:

mvn clean

这将会移除 target 文件夹以及构建的数据,保证在下个命令执行前项目是干净的

如果你想生成 IntelliJ IDEA 的描述文件:

mvn idea:idea

这个命令可以在 IDEA 的项目上运行,更新以前的设置

如果你使用的是 Eclipse:

mvn eclipse:eclipse

要注意的是,一些 Maven 1.0 的目标仍旧可以使用,比如 jar:jar,但是它的执行结果跟你想的不一样,jar:jar 不会重新编译,它只是从 target/classes 文件夹创建一个 jar 文件出来,默认你已经把之前的所有步骤都做好。

SNAPSHOT

你会看到 pom.xml 中 version 标签有 -SNAPSHOT 后缀

<project xmlns="http://maven.apache.org/POM/4.0.0"
    ...
    <groupId>...</groupId>
    <artifactId>my-app</artifactId>
    ...
    <version>1.0-SNAPSHOT</version>
    <name>Maven Quick Start Archetype</name>
    ...

SNAPSHOT 表示一个开发分支上的最新代码,但是不保证这个代码是稳定的或者不变的,相反的,在正式版本(version 标签的值没有 SNAPSHOT 后缀)里的代码是不会改变的

在发布过程中,x.y-SNAPSHOT 版本会变成 x.y 的版本,并且把开发版本号+1,变为 x.(y+1)-SNAPSHOT,比如,1.0-SNAPSHOT版本发布的正式版本号是 1.0,下一个开发版本号就是 1.1-SNAPSHOT

使用插件

当你想自定义 Maven 项目的构建,你可以添加或重新配置插件,像下面这样:

...
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.3</version>
            <configuration>
                <source>1.5</source>
                <target>1.5</target>
            </configuration>
        </plugin>
    </plugins>
</build>
...

看上去和依赖有点像,换句话说,插件也是依赖,会被自动的下载并使用,你需要的话也可以指定版本号,默认是最新版本

configuration 元素的值会被用在所有的编译器插件上

往 JAR 文件里面添加资源

我们不需要去修改上面的 POM 文件就可以做到,Maven 通过标准的目录结构来实现打包 JAR 文件里的资源的功能

我们在想要被打包进 jar 文件的目录下放了目录 ${basedir}/src/main/resources,Maven 提供的规则是所有放在 ${basedir}/src/main/resources 目录下的文件或文件夹都会按照相同的路径被放在 jar 文件的根目录下

比如:

my-app
|-- pom.xml
`-- src
    |-- main
    |   |-- java
    |   |   `-- com
    |   |       `-- mycompany
    |   |           `-- app
    |   |               `-- App.java
    |   `-- resources
    |       `-- META-INF
    |           `-- application.properties
    `-- test
        `-- java
            `-- com
                `-- mycompany
                    `-- app
                        `-- AppTest.java

我们在上面提到的目录下放了 META-INF/application.properties 文件,你可以把生成的 jar 文件解压一下看看:

|-- META-INF
|   |-- MANIFEST.MF
|   |-- application.properties
|   `-- maven
|       `-- com.mycompany.app
|           `-- my-app
|               |-- pom.properties
|               `-- pom.xml
`-- com
    `-- mycompany
        `-- app
            `-- App.class

你也能看到其他 Maven 约定的文件:META-INF/MANIFEST.MFpom.xmlpom.properties,这些都是 Maven 在 JAR 文件里的标准文件,你可以自定义 manifest 或者 Maven 会生成一个默认的,你也可以直接修改默认的 manifest 里的条目,pom.xml 和 pom.properties是被打包进 jar 的,所以 Maven 制造的每一个项目都有自己的描述并允许你在自己的应用中使用里面的元数据,最常用的是检索你应用的版本号,操作 POM 文件需要你使用 Maven 工具,但是可用的属性可以直接使用标准的 JAVA API 访问:

#Generated by Maven
#Tue Oct 04 15:43:21 GMT-05:00 2005
version=1.0-SNAPSHOT
groupId=com.mycompany.app
artifactId=my-app

往你的单元测试里增加资源的方法和上面几乎一样,不同的是,添加资源的目录是 ${basedir}/src/test/resources

my-app
|-- pom.xml
`-- src
    |-- main
    |   |-- java
    |   |   `-- com
    |   |       `-- mycompany
    |   |           `-- app
    |   |               `-- App.java
    |   `-- resources
    |       `-- META-INF
    |           |-- application.properties
    `-- test
        |-- java
        |   `-- com
        |       `-- mycompany
        |           `-- app
        |               `-- AppTest.java
        `-- resources
            `-- test.properties

在单元测试中你可以使用简单的代码去访问测试需要的资源:

...
 
// Retrieve resource
InputStream is = getClass().getResourceAsStream( "/test.properties" );
 
// Do something with the resource
 
...

过滤文件中的内容

如果你想取引用一些你的资源文件里的属性的时候,你可以使用:

${<property name>}

这个属性(property)可以定义在你的 pom.xml 或者你的 settings.xml 或者是额外的属性文件,或者是系统属性

如果要让 Maven 在复制过程中去过滤文件,你只需要把 pom.xml 中的 filtering 的值设为 true

<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.mycompany.app</groupId>
    <artifactId>my-app</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
 
    <name>Maven Quick Start Archetype</name>
    <url>http://maven.apache.org</url>
 
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
 
    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>
</project>

要注意,filtering 的默认值是 false,所以你需要在 pom.xml 中声明它为 true

当你引用定义在 pom.xml 中的属性时,应该使用定义了这个值的 xml 元素,因为 pom 的别名也可以是 project 根元素,所以 ${project.name} 代表项目的名称,${project.version} 代表项目的版本,${project.build.finalName} 代表项目打包后的最终文件名。

你也可以在不明确指明时使用那些 pom.xml 中的默认值,同样的,在 settings.xml 中定义的属性也能通过 settings 的前缀被引用,像 ${settings.localRepository} 代表着用户本地的仓库。

在 src/main/resources/application.properties 文件中添加两个属性,这两个值可以在过滤时被引用到:

# application.properties
application.name=${project.name}
application.version=${project.version}

然后执行下面的命令:

mvn process-resources

你可以在 target/classes/application.properties 看到:

# application.properties
application.name=Maven Quick Start Archetype
application.version=1.0-SNAPSHOT

如果你想要去引用一个外部文件中定义的属性,你要在你的 pom.xml 文件中添加这个外部文件。

先创建一个文件 src/main/filters/filter.properties :

# filter.properties
my.filter.value=hello!

在 pom.xml 中添加这个新文件:

<project>
···
    <build>
        <filters>
            <filter>src/main/filters/filter.properties</filter>
        </filters>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>
</project>

接下来在 application.properties 中引用:

# application.properties
application.name=${project.name}
application.version=${project.version}
message=${my.filter.value}

再次执行 mvn process-resources 你就能在 application.properties 中看到引用的值了。

你也可以在 pom.xml 的 properties 节点中定义值:

<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">
    
    ···
    
    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>
 
    <properties>
        <my.filter.value>hello</my.filter.value>
    </properties>
</project>

你也可以在文件中引用系统属性,像 java 中构建的系统属性:java.version 和 user.home 或者是使用 Java -D 参数在命令行中定义的属性:

# application.properties
java.version=${java.version}
command.line.prop=${command.line.prop}

然后在命令行中执行:

mvn process-resources "-Dcommand.line.prop=hello again"

依赖

在 pom.xml 中 dependencies 的节点里列举出了所有我们在构建项目时会用到的依赖

引入 JUnit 的依赖:

<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.mycompany.app</groupId>
    <artifactId>my-app</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
 
    <name>Maven Quick Start Archetype</name>
    <url>http://maven.apache.org</url>
 
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

对于每一个依赖,你需要定义至少四个属性: groupId,artifactId,version,scope

scope 元素指明你的项目怎么去用这个依赖,它的值可以是:

  • compile
  • test
  • runtime

Maven 会在构建项目时在本地仓库(默认是 ${user.home}/.m2/repository )寻找你引用的依赖

我们在之前打包到本地的 my-app-1.0-SNAPSHOT.jar 项目可以这样引用:

<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">
    
    <groupId>com.mycompany.app</groupId>
    <artifactId>my-other-app</artifactId>
    ...
    <dependencies>
        ...
        <dependency>
            <groupId>com.mycompany.app</groupId>
            <artifactId>my-app</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>
</project>

而当本地仓库没有项目要引用的依赖时,Maven 会去远程仓库查找并把依赖下载到本地仓库,默认的远程仓库是 http://repo.maven.apache.org/maven2/,当然你也可以自己创建一个远程仓库,这个之后再讲

举个例子,来给代码加一些日志,我们需要添加一个 log4j 的依赖,我们需要去查到 log4j 的 groupId,artifactId 和 version

我们可以看到网上有很多版本的依赖,在这里我们可以去查一下当前的正式版或者最新版,但是在 Maven 引用的时候,会选用最新的发布版下载到本地

把依赖的引用放进 pom.xml 文件的 dependencies 节点中:

<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.12</version>
    <scope>compile</scope>
</dependency>

然后执行 mvn compile,就会看到 Maven 在自己下载依赖

把自己的 jar 包部署到远程仓库

  • 在 pom.xml 中配置远程仓库
  • 在 settings.xml 中配置作者信息

下面给出一个使用 scp(scp就是secure copy,是用来进行远程文件拷贝的。数据传输使用 ssh,并且和ssh 使用相同的认证方式,提供相同的安全保证 。 与rcp 不同的是,scp 在需要进行验证时会要求你输入密码或口令。)和用户名/密码验证登陆的例子:

# pom.xml
<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.mycompany.app</groupId>
    <artifactId>my-app</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
 
    <name>Maven Quick Start Archetype</name>
    <url>http://maven.apache.org</url>
 
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.codehaus.plexus</groupId>
            <artifactId>plexus-utils</artifactId>
            <version>1.0.4</version>
        </dependency>
    </dependencies>
    
    <build>
        <filters>
            <filter>src/main/filters/filters.properties</filter>
        </filters>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>
    
    <distributionManagement>
        <repository>
            <id>mycompany-repository</id>
            <name>MyCompany Repository</name>
            <url>scp://repository.mycompany.com/repository/maven2</url>
        </repository>
    </distributionManagement>
</project>
# settings.xml
<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">
    
    ...
    
    <servers>
        <server>
            <id>mycompany-repository</id>
            <username>jvanzyl</username>
            <!-- Default value is ~/.ssh/id_dsa -->
            <privateKey>/path/to/identity</privateKey> (default is ~/.ssh/id_dsa)
            <passphrase>my_key_passphrase</passphrase>
        </server>
    </servers>
    ...
</settings>

注意:如果你要连接的 ssh 服务器的在 ssh_config 文件里的 PasswordAuthentication 参数的值为 no,在每次需要验证用户/密码的时候都要输入密码,

生成其他类型的包

只需要修改 pom.xml 文件中的 packaging 元素的值为 war,然后执行

mvn clean package

你就能看到 target/my-webapp.war 文件的构建过程了


原文地址: http://maven.apache.org/guides/getting-started/index.html

非全文翻译,水平有限,仅供参考,欢迎交流~~