@Otokaze
2018-11-23T19:39:25.000000Z
字数 53532
阅读 986
Java
Maven 是 Apache 开发的 项目管理 和 自动构建 工具,使用 Java 语言开发,主要也是用于管理 Java 项目(特别是 Java Web 项目)。
生命周期(lifecycle)、阶段(phase)、插件(plugin)、目标(goal)
生命周期 是由一系列 阶段 组成的,maven 中定义了 3 个生命周期:clean
、default
、site
。不同的生命周期之间是互相独立的、互不影响的。clean 生命周期负责项目清理,default 生命周期负责项目构建,site 生命周期负责项目报表。
生命周期内部的不同 阶段 之间是有顺序的,后面的阶段依赖于前面的阶段。比如存在阶段 A、B、C,当我们执行阶段 C 时,实际上是执行的 A、B、C 三个阶段,以此类推,当我们执行阶段 B 时,实际上执行的是 A、B 两个阶段。
生命周期和阶段都是抽象概念,因为它们实际上并不做任何事,真正做事的是插件和目标(实现)。
插件 的概念很好理解,插件中有一个或多个 目标(goal,可以理解为功能),也可以将插件理解为多个目标的集合。插件中的目标是实际干活的东西,一个阶段可以与一个或多个目标绑定,这样当我们调用某个阶段时,maven 会自动执行对应的目标组。如果一个阶段没有与任何目标相绑定,那么调用这个阶段实际上不会做任何事,即它是一个“空调用”。
我们可以这样理解,phase 是接口/抽象类,goal 是具体的实现类;接口仅仅起到一个规范的作用,实际的操作都是由实现类来完成的,而我们调用它们时,通常也是调用接口,而不是实现类,因为我们要依赖于抽象,而不是依赖于细节。在 maven 中也是一个道理,我们通常不会去调用 goal,而是调用 phase;因为单独调用 goal 会破坏 maven 的不同阶段之间的依赖关系,而调用 phase 的话则不会,这样我们就能够做到调用 C 阶段时,同时完成了调用 A、B、C 三个阶段。如果你仅仅是调用 C 阶段对应的 goal,那么实际上 maven 并不会调用 A、B 阶段对应的 goals。这样往往会导致某些问题。
maven 为了做到开箱即用,将常用的 phase 映射到了默认的 goal,这样我们在使用 maven 时就不需要自己定义常用的 phase 与 goal 之间的映射关系了。比如 mvn clean
,调用的是 clean 生命周期中的 clean 阶段,而 clean 阶段默认映射到 maven-clean-plugin:3.0.0:clean
目标,即调用的是 clean 插件的 clean 目标,实际的清理工作都是由具体的目标来执行的,一个阶段对应的目标可以有 0 个、1 个、多个;如果有 0 个目标,那么这个阶段不会做任何事,如果有一个目标,那么这个阶段就是调用对应的那个目标来干事,如果有多个目标,那么 maven 会按照 pom.xml 中的目标声明的顺序来依次调用它们。
所以,调用 mvn clean
实际上就相当于调用 mvn clean:clean
,前面是插件名称,后面是目标名称。当然严格来说不是这样的,因为 clean 之前还有 pre-clean 阶段,所以 maven 会先执行 pre-clean 对应的目标,但是因为 pre-clean 没有默认映射到的目标,所以基本上还是符合上面所说的。
总结:maven 的所有工作都是由具体的插件的目标来完成的,我们使用 maven 时,通常不会直接调用插件的目标,而是调用 maven 定义的阶段(可以由多个阶段,它们会按照顺序依次执行),因为阶段实际上会被映射到具体的目标,这样做的目的是为了减轻使用者的负担;而且可以依靠阶段之间的顺序来完成一系列的事,这是直接调用目标所做不到的事。比如执行 mvn package
,调用 default 生命周期的 package 阶段,因为 package 之前还有很多其他阶段,所以 maven 会先执行它前面的阶段,然后才会执行 package 阶段(再罗嗦一下,执行阶段就是执行阶段对应的目标,一个阶段可以对应零个、一个、多个目标,如果是零个目标,那么这个阶段不会做任何事)。而如果我们直接执行 mvn jar:jar
或 mvn war:war
或 mvn ear:ear
这几个 package 阶段对应的目标,那么它实际上并不会执行 package 阶段前面的任何阶段,而是单纯的执行这几个目标罢了(破坏了阶段的顺序)。
maven 允许我们自定义 lifecycle、phase、plugin、goal(这是当然的),即使是我们自定义,我们也应该调用 phase,而不是直接调用 goal,因为这会直接破坏 maven 内部的不同阶段的依赖关系。
mvn <phase-name> ...
mvn <plugin-name>:<goal-name> ...
mvn <plugin-name>:help ...
mvn <phase-name>... <goal-name>...
上面的 mvn <plugin-name>:<goal-name>
中的 pulgin-name 其实是 plugin prefix。
mvn 命令行中还可以指定相关的 options 选项,它们可以放在 phase、goal 前面或后面。
maven 的配置文件为 pom.xml
(项目对象模型),它位于项目的根目录。和 make 一样,pom.xml 与 makefile 承担一样的角色,所有与 maven 项目相关的配置都是位于 pom.xml 项目对象模型文件中。
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">
<!-- pom.xml 版本号 -->
<modelVersion>4.0.0</modelVersion>
<!-- 项目坐标,三元组 -->
<groupId>...</groupId>
<artifactId>...</artifactId>
<version>...</version>
<!-- 项目的打包格式 -->
<packaging>...</packaging>
<!-- 项目的属性配置 -->
<properties>...</properties>
<!-- 项目的依赖配置 -->
<dependencies>...</dependencies>
<dependencyManagement>...</dependencyManagement>
<!-- 项目继承与聚合 -->
<parent>...</parent>
<modules>...</modules>
<!-- 项目的环境配置 -->
<profiles>...</profiles>
<!-- 项目的仓库配置 -->
<repositories>...</repositories>
<pluginRepositories>...</pluginRepositories>
<!-- 项目的构建配置 -->
<build>...</build>
<!-- 项目的报告配置 -->
<reporting>...</reporting>
<!-- 项目的描述信息 -->
<url>...</url>
<name>...</name>
<licenses>...</licenses>
<description>...</description>
<inceptionYear>...</inceptionYear>
<organization>...</organization>
<developers>...</developers>
<contributors>...</contributors>
<!-- 项目的其他信息 -->
<scm>...</scm>
<mailingLists>...</mailingLists>
<prerequisites>...</prerequisites>
<ciManagement>...</ciManagement>
<issueManagement>...</issueManagement>
<distributionManagement>...</distributionManagement>
</project>
最小的 pom.xml
<project>
<modelVersion>4.0.0</modelVersion> <!-- 文件的版本 -->
<groupId>com.zfl9</groupId> <!-- 项目的组名 -->
<artifactId>hello</artifactId> <!-- 项目的名称 -->
<version>1.0</version> <!-- 项目的版本 -->
</project>
其中,groupId
、artifactId
、version
被称为 maven 的坐标,maven 可以通过这三个信息(被称为三元组)唯一的定位任何一个 maven 项目。modelVersion 是 pom.xml 版本号,固定为 4.0.0
。
pom.xml 的继承
pom.xml 和 Java 中的 OOP 一样,存在继承关系,并且它们都是单继承的。和 Java 一样,如果一个 pom.xml 没有明确指定它的父 pom,那么该 pom.xml 的父 pom 就是 super pom(和 Java 中的 java.lang.Object 类一样,它是 Java 中所有类的基类)。
同样的,有继承就有重写(覆盖),子 pom 的相同配置可以覆盖父 pom 中的相同配置,这在 Java 中叫做 Override,在 maven 中叫做配置覆盖。maven 很好的利用了这一点,它在 super pom 中定义了很多默认的配置,这样我们就可以编写简短的 pom.xml 来完成很多复杂的任务。
在 maven 中,术语 effective-pom
表示当前项目的父 pom 和当前的 pom 相结合的 pom(即经过覆盖后的实际生效的 pom 配置),我们可以通过运行 mvn help:effective-pom
来查看当前项目的 effective-pom 配置。比如我们可以创建一个最小的 pom.xml,然后执行这个命令,就能知道 maven 中的 super pom 的具体配置信息,如下。
<?xml version="1.0" encoding="UTF-8"?>
<project>
<!-- 最小 pom.xml -->
<modelVersion>4.0.0</modelVersion>
<groupId>com.zfl9</groupId>
<artifactId>hello</artifactId>
<version>1.0</version>
<!-- 项目的属性配置 -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!-- 主要仓库的信息 -->
<repositories>
<repository>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>central</id>
<name>Central Repository</name>
<url>https://repo.maven.apache.org/maven2</url>
</repository>
</repositories>
<!-- 插件仓库的信息 -->
<pluginRepositories>
<pluginRepository>
<releases>
<updatePolicy>never</updatePolicy>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>central</id>
<name>Central Repository</name>
<url>https://repo.maven.apache.org/maven2</url>
</pluginRepository>
</pluginRepositories>
<!-- 项目的主体配置 -->
<build>
<!-- 项目的最终名称 -->
<finalName>hello-1.0</finalName>
<!-- 项目的 source 目录 -->
<sourceDirectory>/root/maven-workspace/src/main/java</sourceDirectory>
<scriptSourceDirectory>/root/maven-workspace/src/main/scripts</scriptSourceDirectory>
<testSourceDirectory>/root/maven-workspace/src/test/java</testSourceDirectory>
<!-- 项目的 target 目录 -->
<directory>/root/maven-workspace/target</directory>
<outputDirectory>/root/maven-workspace/target/classes</outputDirectory>
<testOutputDirectory>/root/maven-workspace/target/test-classes</testOutputDirectory>
<!-- 项目的 main 资源目录 -->
<resources>
<resource>
<directory>/root/maven-workspace/src/main/resources</directory>
</resource>
</resources>
<!-- 项目的 test 资源目录 -->
<testResources>
<testResource>
<directory>/root/maven-workspace/src/test/resources</directory>
</testResource>
</testResources>
<!-- plugin 相关插件的声明 (只是声明) -->
<pluginManagement>
<plugins>
<!-- antrun 插件 -->
<plugin>
<!-- maven 官方插件可以省略 groupId -->
<artifactId>maven-antrun-plugin</artifactId>
<version>1.3</version>
</plugin>
<!-- assembly 插件 -->
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2-beta-5</version>
</plugin>
<!-- dependency 插件 -->
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.8</version>
</plugin>
<!-- release 插件 -->
<plugin>
<artifactId>maven-release-plugin</artifactId>
<version>2.5.3</version>
</plugin>
</plugins>
</pluginManagement>
<!-- plugin 相关插件的标签 (实际用的) -->
<plugins>
<!-- clean 插件 -->
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>2.5</version>
<executions>
<execution>
<id>default-clean</id>
<phase>clean</phase> <!-- 对应的阶段 -->
<goals>
<goal>clean</goal> <!-- 对应的目标 -->
</goals>
</execution>
</executions>
</plugin>
<!-- resources 插件 -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>2.6</version>
<executions>
<execution>
<id>default-testResources</id>
<phase>process-test-resources</phase>
<goals>
<goal>testResources</goal>
</goals>
</execution>
<execution>
<id>default-resources</id>
<phase>process-resources</phase>
<goals>
<goal>resources</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- jar 插件 -->
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<executions>
<execution>
<id>default-jar</id>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- compiler 插件 -->
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<executions>
<execution>
<id>default-compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>default-testCompile</id>
<phase>test-compile</phase>
<goals>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- surefire 插件 -->
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.12.4</version>
<executions>
<execution>
<id>default-test</id>
<phase>test</phase>
<goals>
<goal>test</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- install 插件 -->
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.4</version>
<executions>
<execution>
<id>default-install</id>
<phase>install</phase>
<goals>
<goal>install</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- deploy 插件 -->
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.7</version>
<executions>
<execution>
<id>default-deploy</id>
<phase>deploy</phase>
<goals>
<goal>deploy</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- site 插件 -->
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.3</version>
<executions>
<execution>
<id>default-site</id>
<phase>site</phase>
<goals>
<goal>site</goal>
</goals>
<configuration>
<outputDirectory>/root/maven-workspace/target/site</outputDirectory>
<reportPlugins>
<reportPlugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-project-info-reports-plugin</artifactId>
</reportPlugin>
</reportPlugins>
</configuration>
</execution>
<execution>
<id>default-deploy</id>
<phase>site-deploy</phase>
<goals>
<goal>deploy</goal>
</goals>
<configuration>
<outputDirectory>/root/maven-workspace/target/site</outputDirectory>
<reportPlugins>
<reportPlugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-project-info-reports-plugin</artifactId>
</reportPlugin>
</reportPlugins>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<!-- 项目的报表配置 -->
<reporting>
<outputDirectory>/root/maven-workspace/target/site</outputDirectory>
</reporting>
</project>
详解 pom 的三元组
groupId
:组名,在某种程度上可以将 groupId 理解为 package 包名。artifactId
:项目名,其实就是 maven 项目的根目录的文件夹名称而已。version
:项目的版本号,版本号很好理解,有了版本号更利于项目的讨论。而 modelVersion 模型版本在目前来说,始终是 4.0.0,这是目前唯一的版本号。
maven 中使用格式 groupId:artifactId:version
来描述一个具体的项目,比如:
org.apache:tomcat:8.5
,表示 org.apache 开发的 tomcat,它的版本号是 8.5。
clean 生命周期的常用阶段
clean
:执行项目的清理工作。default 生命周期的常用阶段
阶段 | 描述 |
---|---|
验证 validate |
验证项目是否正确且所有必须信息是可用的 |
编译 compile |
项目代码和测试代码的编译是在此阶段完成的 |
测试 test |
使用适当的单元测试框架(junit)运行测试 |
包装 package |
将项目打包为 JAR/WAR/EAR 包,以方便部署 |
检查 verify |
对集成测试的结果进行检查,以保证质量达标 |
安装 install |
安装打包的项目到本地仓库,以供其他项目使用 |
部署 deploy |
拷贝项目包到远程仓库中,以共享给其他开发人员 |
site 生命周期的常用阶段
site
:生成项目的站点文档(或者是项目报表)site-deploy
:将文档部署到指定 web 服务器上maven 中的任何一个依赖、插件、项目都是从仓库中获取的,maven 中有 3 种类型的仓库:
maven 在获取任何一个依赖、插件、项目时都会按照上述顺序依次查找,如果都没有找到则报错。
本地仓库
在 windows、linux 中,本地仓库的位置位于 $HOME/.m2/respository/
目录,如果不存在这个目录,那么 maven 第一次运行时会自动创建它。当 maven 需要获取任何一个构件时,它都将首先从本地仓库中搜寻,如果没有找到,则再去中央仓库中搜寻,如果中央仓库也没有,则会去远程仓库中搜寻,如果还没有那么就只能爆出错误了;如果在某个步骤中搜寻到了,那么 maven 会将这些构件保存到本地仓库中,这样以后再获取这些构件时就不用去别的地方找了,因为他们直接就在本地仓库中被找到了,这也是问什么第一次运行 maven 命令时很慢,而之后再次运行同样的命令就很快的原因了。
中央仓库
maven 中央仓库是由 maven 社区提供的,中央仓库中基本涵盖了目前所有流行的开源构件,一般来说项目中所依赖到的构件都可以在中央仓库中获取到。但因为中央仓库位于网络,所以如果想要获取中央仓库上的构件,你首先得确保你的主机能够连上互联网。而且因为 maven 中央仓库在国外,所以国内访问时难免很慢,如果希望获取构件时可以快一点,可以考虑配置阿里云 maven 镜像之类的。
远程仓库
如果我们还配置了 maven 远程仓库(一般在公司内部),那么当 maven 无法从中央仓库中获取构件时,会尝试从远程仓库中获取,远程仓库一般是公司内部维护的,相比中央仓库,速度更快,而且好管理,并且不需要将相关的构件开源,因为中央仓库上的构件都是要开源的,而公司内部的话,不需要开源,就是自己用而已。
注意,上面的描述有一部分是错误的,maven 中其实只有本地仓库和远程仓库两种。本地仓库就是家目录下的那个文件夹,而远程仓库默认是 maven 的中心仓库,如果我们想提高从远程仓库中获取 maven 构件的速度,可以修改 maven 的远程仓库地址,比如改为阿里云的镜像,这样 maven 在无法从本地仓库中获取构件时会从阿里云镜像仓库中获取而不是 maven 的默认中央仓库。除了这种国内镜像的玩法,我们还有一种玩法,那就是将 maven 的远程仓库改为本地局域网(公司内部),然后这个局域网的仓库(人们喜欢将它称为私服)内部是这么一个工作方式,当 maven 无法从本地仓库获取构件时,会请求远程仓库,即私服,而私服默认安装好里面也是没有任何构件的,所以私服会请求 maven 的中央仓库,然后在缓存到私服,私服在返回给 maven 客户端。同时,我们可以在 maven 私服中上传我们公司内部使用的 maven 构件,然后 maven 客户端就可以从私服中获取公司内部使用的 maven 构件,而不必将 maven 构件开源,上传到 maven 中央仓库。
私服的好处有两个:一是能够缓存中央仓库的 maven 构件,这样能够减少网络请求的负担;二是能够上传公司内部使用的专属 maven 构件,方便管理和使用。
maven 中有两个重要的概念:phase(阶段)、goal(目标)。phase 是生命周期中的概念,goal 是插件中的概念。phase 是抽象的,goal 是具体的。phase 实际上不会做任何事,它仅仅起到一个接口的作用,而实际上的操作都是由插件的目标来完成的,goal 实现了 phase 对应的接口,我们调用 phase 时,其实就是调用 phase 对应的 goal(s)。
生命周期是阶段的集合,插件是目标的集合,生命周期是抽象概念,插件是具体概念。maven 实际上是一个依赖于插件的执行框架,插件是一等公民,没有了插件,maven 什么都不是,因为他做不了任何事,所有的工作其实都是由插件完成的。
maven 提供了以下两种类型的插件:
<build>
标签中定义。<reporting>
标签中定义。maven 使用原型(archetype)插件来创建项目,注意是插件哦,因为 maven 里面万物基于插件。原型是什么东西呢?你可以将 archetype 理解为 template 模板,就像 html 模板一样,如果我们要开发一个网站,一般我们会先去网上找的适合的 html 模板,然后在模板的基础上进行修改,最后就做成了我们的网站了。
在 maven 中也是一样的,使用 maven 创建项目一般也是通过 archetype 来创建的,archetype 就是一个模板,archetype 基本上都是 java 的最佳实践,是有良好设计的模板,通过使用 archetype,大家可以很容易理解一个项目,因为这是最佳实践,而且大家的项目结构都是一样的,便于沟通和交流。
archetype 是一个插件,它是 maven 官方提供的,通过运行 mvn archetype:generate
命令,mvn 会列出目前所有的模板,截止 2018-10-24 日,已经有 2263 个 archetypes 了,通过这些模板,我们能够很快的构建一个项目的骨架,然后就能够专心的进行 Java 开发,最后使用 mvn 的各种阶段,进行项目的管理。
maven 官方提供的几个原型
maven-archetype-webapp
:Java Web 项目(Java Web)maven-archetype-quickstart
:Java 普通项目(Java SE)我们先来看看 quickstart 原型:
# root @ arch in ~/maven-workspace [10:07:08]
$ mvn -B archetype:generate -DarchetypeArtifactId=maven-archetype-quickstart -DgroupId=com.zfl9 -DartifactId=javase
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------< org.apache.maven:standalone-pom >-------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] --------------------------------[ pom ]---------------------------------
[INFO]
[INFO] >>> maven-archetype-plugin:3.0.1:generate (default-cli) > generate-sources @ standalone-pom >>>
[INFO]
[INFO] <<< maven-archetype-plugin:3.0.1:generate (default-cli) < generate-sources @ standalone-pom <<<
[INFO]
[INFO]
[INFO] --- maven-archetype-plugin:3.0.1:generate (default-cli) @ standalone-pom ---
[INFO] Generating project in Batch mode
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Old (1.x) Archetype: maven-archetype-quickstart:1.0
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: basedir, Value: /root/maven-workspace
[INFO] Parameter: package, Value: com.zfl9
[INFO] Parameter: groupId, Value: com.zfl9
[INFO] Parameter: artifactId, Value: javase
[INFO] Parameter: packageName, Value: com.zfl9
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] project created from Old (1.x) Archetype in dir: /root/maven-workspace/javase
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 5.053 s
[INFO] Finished at: 2018-10-24T10:08:05+08:00
[INFO] ------------------------------------------------------------------------
# root @ arch in ~/maven-workspace [10:08:05]
$ cd javase
# root @ arch in ~/maven-workspace/javase [10:08:08]
$ tree
.
├── pom.xml
└── src
├── main
│ └── java
│ └── com
│ └── zfl9
│ └── App.java
└── test
└── java
└── com
└── zfl9
└── AppTest.java
9 directories, 3 files
# root @ arch in ~/maven-workspace/javase [10:08:10]
$ cat src/main/java/com/zfl9/App.java
package com.zfl9;
/**
* Hello world!
*
*/
public class App
{
public static void main( String[] args )
{
System.out.println( "Hello World!" );
}
}
# root @ arch in ~/maven-workspace/javase [10:08:16]
$ mvn package
[INFO] Scanning for projects...
[INFO]
[INFO] --------------------------< com.zfl9:javase >---------------------------
[INFO] Building javase 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ javase ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /root/maven-workspace/javase/src/main/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ javase ---
[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 /root/maven-workspace/javase/target/classes
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ javase ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /root/maven-workspace/javase/src/test/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ javase ---
[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 /root/maven-workspace/javase/target/test-classes
[INFO]
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ javase ---
[INFO] Surefire report directory: /root/maven-workspace/javase/target/surefire-reports
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running com.zfl9.AppTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.01 sec
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ javase ---
[INFO] Building jar: /root/maven-workspace/javase/target/javase-1.0-SNAPSHOT.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.585 s
[INFO] Finished at: 2018-10-24T10:08:22+08:00
[INFO] ------------------------------------------------------------------------
# root @ arch in ~/maven-workspace/javase [10:08:22]
$ tree
.
├── pom.xml
├── src
│ ├── main
│ │ └── java
│ │ └── com
│ │ └── zfl9
│ │ └── App.java
│ └── test
│ └── java
│ └── com
│ └── zfl9
│ └── AppTest.java
└── target
├── classes
│ └── com
│ └── zfl9
│ └── App.class
├── javase-1.0-SNAPSHOT.jar
├── maven-archiver
│ └── pom.properties
├── maven-status
│ └── maven-compiler-plugin
│ ├── compile
│ │ └── default-compile
│ │ ├── createdFiles.lst
│ │ └── inputFiles.lst
│ └── testCompile
│ └── default-testCompile
│ ├── createdFiles.lst
│ └── inputFiles.lst
├── surefire-reports
│ ├── com.zfl9.AppTest.txt
│ └── TEST-com.zfl9.AppTest.xml
└── test-classes
└── com
└── zfl9
└── AppTest.class
24 directories, 13 files
# root @ arch in ~/maven-workspace/javase [10:08:27]
$ java -cp target/javase-1.0-SNAPSHOT.jar com.zfl9.App
Hello World!
我们来看看 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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.zfl9</groupId>
<artifactId>javase</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>javase</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>
其实我们现在用不到 test,所以我们来精简一下:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.zfl9</groupId>
<artifactId>javase</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
</project>
然后我们再来看下(我删掉了 src/test
目录,因为用不到):
# root @ arch in ~/maven-workspace/javase [10:15:10]
$ tree
.
├── pom.xml
└── src
└── main
└── java
└── com
└── zfl9
└── App.java
5 directories, 2 files
# root @ arch in ~/maven-workspace/javase [10:15:11]
$ mvn package
[INFO] Scanning for projects...
[INFO]
[INFO] --------------------------< com.zfl9:javase >---------------------------
[INFO] Building javase 1.0
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ javase ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /root/maven-workspace/javase/src/main/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ javase ---
[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 /root/maven-workspace/javase/target/classes
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ javase ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /root/maven-workspace/javase/src/test/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ javase ---
[INFO] No sources to compile
[INFO]
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ javase ---
[INFO] No tests to run.
[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ javase ---
[INFO] Building jar: /root/maven-workspace/javase/target/javase-1.0.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.136 s
[INFO] Finished at: 2018-10-24T10:15:18+08:00
[INFO] ------------------------------------------------------------------------
# root @ arch in ~/maven-workspace/javase [10:15:18]
$ tree
.
├── pom.xml
├── src
│ └── main
│ └── java
│ └── com
│ └── zfl9
│ └── App.java
└── target
├── classes
│ └── com
│ └── zfl9
│ └── App.class
├── javase-1.0.jar
├── maven-archiver
│ └── pom.properties
└── maven-status
└── maven-compiler-plugin
└── compile
└── default-compile
├── createdFiles.lst
└── inputFiles.lst
14 directories, 7 files
# root @ arch in ~/maven-workspace/javase [10:15:22]
$ java -cp target/javase-1.0.jar com.zfl9.App
Hello World!
细心的同学可能发现了,mvn 在执行 package 时总是会提示一个字符编码的警告,实际上我们可以通过 pom.xml 文件的一个配置来解决它:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.zfl9</groupId>
<artifactId>javase</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
然后,我们再来看下:
# root @ arch in ~/maven-workspace/javase [10:18:06]
$ mvn clean package
[INFO] Scanning for projects...
[INFO]
[INFO] --------------------------< com.zfl9:javase >---------------------------
[INFO] Building javase 1.0
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ javase ---
[INFO] Deleting /root/maven-workspace/javase/target
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ javase ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /root/maven-workspace/javase/src/main/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ javase ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /root/maven-workspace/javase/target/classes
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ javase ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /root/maven-workspace/javase/src/test/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ javase ---
[INFO] No sources to compile
[INFO]
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ javase ---
[INFO] No tests to run.
[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ javase ---
[INFO] Building jar: /root/maven-workspace/javase/target/javase-1.0.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.208 s
[INFO] Finished at: 2018-10-24T10:18:13+08:00
[INFO] ------------------------------------------------------------------------
# root @ arch in ~/maven-workspace/javase [10:18:13]
$ java -cp target/javase-1.0.jar com.zfl9.App
Hello World!
没有烦人的警告信息了,真爽(强迫症福音啊)。
但是,我们还没有彻底的干掉 test 步骤,修改 pom.xml:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.zfl9</groupId>
<artifactId>javase</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<properties>
<maven.test.skip>true</maven.test.skip>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
再来看一下:
# root @ arch in ~/maven-workspace/javase [10:28:28]
$ mvn clean package
[INFO] Scanning for projects...
[INFO]
[INFO] --------------------------< com.zfl9:javase >---------------------------
[INFO] Building javase 1.0
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ javase ---
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ javase ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /root/maven-workspace/javase/src/main/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ javase ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /root/maven-workspace/javase/target/classes
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ javase ---
[INFO] Not copying test resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ javase ---
[INFO] Not compiling test sources
[INFO]
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ javase ---
[INFO] Tests are skipped.
[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ javase ---
[INFO] Building jar: /root/maven-workspace/javase/target/javase-1.0.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.156 s
[INFO] Finished at: 2018-10-24T10:28:32+08:00
[INFO] ------------------------------------------------------------------------
# root @ arch in ~/maven-workspace/javase [10:28:32]
$ java -cp target/javase-1.0.jar com.zfl9.App
Hello World!
好吧,虽然还是有 test 的一些东西,但是最起码比之前好一点,对 maven 理解更加深入后再说吧。
java web 原型
现在我们来创建 java web 的原型,进行 java web 的开发:
# root @ arch in ~/maven-workspace [10:39:38]
$ mvn -B archetype:generate -DarchetypeArtifactId=maven-archetype-webapp -DgroupId=com.zfl9 -DartifactId=javaweb
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------< org.apache.maven:standalone-pom >-------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] --------------------------------[ pom ]---------------------------------
[INFO]
[INFO] >>> maven-archetype-plugin:3.0.1:generate (default-cli) > generate-sources @ standalone-pom >>>
[INFO]
[INFO] <<< maven-archetype-plugin:3.0.1:generate (default-cli) < generate-sources @ standalone-pom <<<
[INFO]
[INFO]
[INFO] --- maven-archetype-plugin:3.0.1:generate (default-cli) @ standalone-pom ---
[INFO] Generating project in Batch mode
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Old (1.x) Archetype: maven-archetype-webapp:1.0
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: basedir, Value: /root/maven-workspace
[INFO] Parameter: package, Value: com.zfl9
[INFO] Parameter: groupId, Value: com.zfl9
[INFO] Parameter: artifactId, Value: javaweb
[INFO] Parameter: packageName, Value: com.zfl9
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] project created from Old (1.x) Archetype in dir: /root/maven-workspace/javaweb
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4.436 s
[INFO] Finished at: 2018-10-24T10:39:49+08:00
[INFO] ------------------------------------------------------------------------
# root @ arch in ~/maven-workspace [10:39:49]
$ cd javaweb
# root @ arch in ~/maven-workspace/javaweb [10:39:54]
$ tree
.
├── pom.xml
└── src
└── main
├── resources
└── webapp
├── index.jsp
└── WEB-INF
└── web.xml
5 directories, 3 files
# root @ arch in ~/maven-workspace/javaweb [10:39:56]
$ cat 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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.zfl9</groupId>
<artifactId>javaweb</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>javaweb Maven Webapp</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>
<build>
<finalName>javaweb</finalName>
</build>
</project>
同样的,我们先来精简一下 pom.xml 文件的内容:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.zfl9</groupId>
<artifactId>javaweb</artifactId>
<version>1.0</version>
<packaging>war</packaging>
<properties>
<maven.test.skip>true</maven.test.skip>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
然后,重新 clean、package 看下:
# root @ arch in ~/maven-workspace/javaweb [10:42:57]
$ mvn clean package
[INFO] Scanning for projects...
[INFO]
[INFO] --------------------------< com.zfl9:javaweb >--------------------------
[INFO] Building javaweb 1.0
[INFO] --------------------------------[ war ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ javaweb ---
[INFO] Deleting /root/maven-workspace/javaweb/target
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ javaweb ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /root/maven-workspace/javaweb/src/main/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ javaweb ---
[INFO] No sources to compile
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ javaweb ---
[INFO] Not copying test resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ javaweb ---
[INFO] Not compiling test sources
[INFO]
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ javaweb ---
[INFO] Tests are skipped.
[INFO]
[INFO] --- maven-war-plugin:2.2:war (default-war) @ javaweb ---
[INFO] Packaging webapp
[INFO] Assembling webapp [javaweb] in [/root/maven-workspace/javaweb/target/javaweb-1.0]
[INFO] Processing war project
[INFO] Copying webapp resources [/root/maven-workspace/javaweb/src/main/webapp]
[INFO] Webapp assembled in [23 msecs]
[INFO] Building war: /root/maven-workspace/javaweb/target/javaweb-1.0.war
[INFO] WEB-INF/web.xml already added, skipping
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.859 s
[INFO] Finished at: 2018-10-24T10:43:01+08:00
[INFO] ------------------------------------------------------------------------
# root @ arch in ~/maven-workspace/javaweb [10:43:01]
$ tree
.
├── pom.xml
├── src
│ └── main
│ └── webapp
│ ├── index.jsp
│ └── WEB-INF
│ └── web.xml
└── target
├── javaweb-1.0
│ ├── index.jsp
│ ├── META-INF
│ └── WEB-INF
│ ├── classes
│ └── web.xml
├── javaweb-1.0.war
└── maven-archiver
└── pom.properties
10 directories, 7 files
# root @ arch in ~/maven-workspace/javaweb [10:43:06]
$ cp -af target/javaweb-1.0.war ~/tomcat-apps
# root @ arch in ~/maven-workspace/javaweb [10:43:35]
$ tomcat start
Using CATALINA_BASE: /usr/local/tomcat
Using CATALINA_HOME: /usr/local/tomcat
Using CATALINA_TMPDIR: /usr/local/tomcat/temp
Using JRE_HOME: /usr/local/jdk/jdk1.8
Using CLASSPATH: /usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar
Tomcat started.
# root @ arch in ~/maven-workspace/javaweb [10:43:53]
$ curl 127.0.0.1/javaweb-1.0/
<html>
<body>
<h2>Hello World!</h2>
</body>
</html>
pom.xml 中的 <properties>
表示用于定义 pom 的属性,这个属性和 Java 中的 property 是一个意思,你可以将 property 理解为传递给 pom 的参数。在 <properties>
标签中定义的属性可以在 pom.xml 的其他地方进行引用,语法为 ${property-name}
,其实 properties 属性就是 mvn 命令行选项 -Dname=value
传递的属性,它们是同一个“属性”,都可以在 pom.xml 中引用它,语法一样。很容易知道,mvn 命令行指定的属性的优先级比 pom.xml 文件中的 <properties>
标签内指定的属性的优先级更高,所以如果它们之间有相同的属性设置,那么实际生效的是命令行中指定的那个属性值。
我们来通过一个简单的例子,了解 properties 属性的用法(将它理解为变量也许好一点):
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.zfl9.test</groupId>
<artifactId>property-test</artifactId>
<version>v1.0.0</version>
<properties>
<output.string>hello, world! [from pom.xml]</output.string>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<id>test-task</id>
<phase>clean</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
<echo>${output.string}</echo>
</tasks>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
我们创建了一个 antrun 任务,并将它与 clean 生命周期的 clean 阶段绑定了起来,所以当我们调用 clean 阶段时,ant 任务将会被执行,ant 任务的内容很简单,就是输出 ${output.string}
属性的值,我们在 <properties>
元素下面定义了 out.string 属性,当然我们也可以通过 mvn 命令行选项 -D 来定义。OK,我们来演示一下:
root @ arch in ~/maven-workspace [15:43:07]
$ mvn clean
[INFO] Scanning for projects...
[INFO]
[INFO] --------------------< com.zfl9.test:property-test >---------------------
[INFO] Building property-test v1.0.0
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ property-test ---
[INFO]
[INFO] --- maven-antrun-plugin:1.3:run (test-task) @ property-test ---
[INFO] Executing tasks
[echo] hello, world! [from pom.xml]
[INFO] Executed tasks
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.328 s
[INFO] Finished at: 2018-10-24T15:43:09+08:00
[INFO] ------------------------------------------------------------------------
# root @ arch in ~/maven-workspace [15:43:09]
$ mvn clean -Doutput.string='hello, world! [from arguments]'
[INFO] Scanning for projects...
[INFO]
[INFO] --------------------< com.zfl9.test:property-test >---------------------
[INFO] Building property-test v1.0.0
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ property-test ---
[INFO]
[INFO] --- maven-antrun-plugin:1.3:run (test-task) @ property-test ---
[INFO] Executing tasks
[echo] hello, world! [from arguments]
[INFO] Executed tasks
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.343 s
[INFO] Finished at: 2018-10-24T15:43:19+08:00
[INFO] ------------------------------------------------------------------------
看到了区别吗?arg 传递的 property 优先级比 pom.xml 中硬编码的 property 优先级更高,所以当我们定义了 arg 形式的 output.string 属性时,mvn 将会应用它。现在我们来将 pom.xml 中的 <properties>
标签注释掉,来看一下执行结果:
# root @ arch in ~/maven-workspace [15:45:17]
$ mvn clean
[INFO] Scanning for projects...
[INFO]
[INFO] --------------------< com.zfl9.test:property-test >---------------------
[INFO] Building property-test v1.0.0
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ property-test ---
[INFO]
[INFO] --- maven-antrun-plugin:1.3:run (test-task) @ property-test ---
[INFO] Executing tasks
[echo] ${output.string}
[INFO] Executed tasks
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.345 s
[INFO] Finished at: 2018-10-24T15:45:23+08:00
[INFO] ------------------------------------------------------------------------
# root @ arch in ~/maven-workspace [15:45:23]
$ mvn clean -Doutput.string='hello, world! [from arguments]'
[INFO] Scanning for projects...
[INFO]
[INFO] --------------------< com.zfl9.test:property-test >---------------------
[INFO] Building property-test v1.0.0
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ property-test ---
[INFO]
[INFO] --- maven-antrun-plugin:1.3:run (test-task) @ property-test ---
[INFO] Executing tasks
[echo] hello, world! [from arguments]
[INFO] Executed tasks
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.354 s
[INFO] Finished at: 2018-10-24T15:45:28+08:00
[INFO] ------------------------------------------------------------------------
注意,第一次运行时,output.string
属性并没有被定义任何值,但是 maven 也并没有报错,只是原样的输出 [echo] ${output.string}
,所以我们可以知道,当 pom.xml 中引用的属性没有被定义时,maven 会原样的把 ${property-name}
表达式输出,而不会报错。第二次执行时,我们传递了这个属性给 mvn,所以正常输出。
依赖管理是 maven 的一大特色,当我们的项目使用 maven 管理时,我们无需关心如何获取项目的依赖,我们只需简单的在 pom.xml 中告诉 maven 我需要什么依赖(maven 坐标,即 groupId、artifactId、version),然后 maven 就会依次从 [本地仓库]、[中央仓库]、[远程仓库] 中获取对应的依赖,每当 maven 从中央仓库、远程仓库中获取到依赖后,它都会首先将依赖保存到本地仓库中(~/.m2/repository
),当我们 maven 下次再次尝试获取依赖时,首先搜寻本地仓库,发现已经有了,而且坐标是一致的,所以就拿来直接用了,而不用通过网络去其他地方获取依赖。
通过这种集中管理依赖的机制,我们可以同一台主机的多个项目之间共享依赖项目,因为他们都是从本地仓库中获取的,这样可以便于管理,也能够节省存储空间。最原始的获取依赖的方式是通过 baidu、google,然后去官网下载对应的依赖包,然后保存到本地的某个路径,最后还要配置 classpath 变量,才能使用。
我们来通过 archetype 原型来创建一个 javase 项目,它会依赖到 ognl-v2.7.3.jar
,如何找到 maven 中央仓库中的依赖包名称呢(maven 坐标),很简单,maven 官方提供了一个搜索页面,很好用:https://search.maven.org/,我们只需要打开这个页面,键入需要获取的依赖的名称就行,然后找到对应的依赖项,单击一下,右边就会出现 maven 的 pom.xml 配置,直接拷贝过去就行了,真心方便。
javase 的项目原型我们之前已经创建好了,目录为 ~/maven-workspace/javase
,我们 cd 进去,然后配置 pom.xml,将 ognl 的依赖信息填上去,最终的 pom.xml 文件内容如下:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.zfl9</groupId>
<artifactId>javase</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<properties>
<maven.test.skip>true</maven.test.skip>
<exec.mainClass>com.zfl9.Main</exec.mainClass>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>ognl</groupId>
<artifactId>ognl</artifactId>
<version>2.7.3</version>
</dependency>
</dependencies>
</project>
这里说明一下 <exec.mainClass>
属性,因为 maven 下载到的 ognl.jar 依赖包是放到本地仓库中的,所以当我们将项目打包之后,执行 java -cp target/*.jar com.zfl9.Main
时,会提示找不到 Ognl 的类。因为我们没有将本地仓库中的 jar 包添加到 CLASSPATH 环境变量中,所以就出现了这个问题。
那么 exec.mainClass 属性和这个问题有什么联系呢?出现这个问题的原因是因为我们没有将依赖到的 jar 包加入到 CLASSPATH 变量,其实 maven 早就想到了这个问题,所以我们可以使用 maven 的 exec 插件来执行我们的 jar 包,具体的用法是在 package 之后,调用 mvn exec:java -Dexec.mainClass=com.zfl9.Main
,因为 Main 入口类一般是比较固定的,不太会改动,所以我就把这个属性放到了 pom.xml 中,这样我就只需要执行 mvn clean package exec:java
就能一键运行了。是不是很方便?!那么 exec 插件还有什么参数呢?比如如果我想给 Main 传递参数怎么办?很简单,即 mvn exec:java -Dexec.workingdir=/tmp -Dexec.args="-X myproject:dist"
,也就是通过 exec.args
属性来传递参数,很简单吧。
当然除了这种官方的优雅解决方式外,我们还可以通过另一种不是那么优雅的方式,具体怎么做我就不详细说了,因为这不是最佳方式,只能说是一种 hack 行为而已,参考链接:maven 执行 jar 提示没找到对应的类。
抛开这个,我们其实完全可以去掉这个 exec.mainClass
属性,因为我们可以在 jar 包的 META-INF 中的 MANIFEST.MF
文件中配置 Main-Class 属性,这样我们就能直接运行 jar 包了,语法为:java -jar /path/to/file.jar
,很是方便。
那我们该如何配置 maven,让它在执行 package 打包的时候自动添加 Main-Class 配置行呢?稍微动动脑子想想就知道,我们应该对 maven-jar-plugin 这个插件的 jar 目标进行配置,怎么配置呢?如下。实际上不行,在执行到 exec:java 目标时,exec 插件会提示 MainClass 未定义,然后就退出了。这也许是 exec 插件的设计问题,但是我们可以通过配置 exec:java 目标,来告诉他要执行的 Main-Class(不过这样好像还不如直接用上面的方法,直接定义 mainClass 属性,然后 exec:java 目标会引用他),不管了,方法如下:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.zfl9</groupId>
<artifactId>javase</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<properties>
<maven.test.skip>true</maven.test.skip>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>ognl</groupId>
<artifactId>ognl</artifactId>
<version>2.7.3</version>
</dependency>
</dependencies>
<build>
<plugins>
<!--
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<archive>
<manifest>
<mainClass>com.zfl9.Main</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
-->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<executions>
<execution>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>com.zfl9.Main</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
但其实吧,这样反而是自找麻烦,我觉得还不如用回最开始的方案,定义 exec.mainClass 属性算了,即:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.zfl9</groupId>
<artifactId>javase</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<properties>
<maven.test.skip>true</maven.test.skip>
<exec.mainClass>com.zfl9.Main</exec.mainClass>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>ognl</groupId>
<artifactId>ognl</artifactId>
<version>2.7.3</version>
</dependency>
</dependencies>
</project>
然后,我们来测试一下:
# root @ arch in ~/maven-workspace/javase [20:39:58]
$ mvn clean package exec:java
[INFO] Scanning for projects...
[INFO]
[INFO] --------------------------< com.zfl9:javase >---------------------------
[INFO] Building javase 1.0
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ javase ---
[INFO] Deleting /root/maven-workspace/javase/target
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ javase ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /root/maven-workspace/javase/src/main/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ javase ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 2 source files to /root/maven-workspace/javase/target/classes
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ javase ---
[INFO] Not copying test resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ javase ---
[INFO] Not compiling test sources
[INFO]
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ javase ---
[INFO] Tests are skipped.
[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ javase ---
[INFO] Building jar: /root/maven-workspace/javase/target/javase-1.0.jar
[INFO]
[INFO] --- exec-maven-plugin:1.6.0:java (default-cli) @ javase ---
[name=张三, age=24, score=120.00]
[name=张三, age=24, score=120.00]
=================================
[name=张三, age=24, score=120.00]
[name=张三, age=24, score=120.00]
[name=李四, age=23, score=110.00]
[name=李四, age=23, score=110.00]
[name=王五, age=25, score=140.00]
[name=王五, age=25, score=140.00]
=================================
[name=张三, age=24, score=120.00]
[name=张三, age=24, score=120.00]
[name=李四, age=23, score=110.00]
[name=李四, age=23, score=110.00]
[name=王五, age=25, score=140.00]
[name=王五, age=25, score=140.00]
=================================
[name=张三, age=24, score=120.00]
[name=张三, age=24, score=120.00]
[name=李四, age=23, score=110.00]
[name=李四, age=23, score=110.00]
[name=王五, age=25, score=140.00]
[name=王五, age=25, score=140.00]
=================================
[name=张三, age=24, score=120.00]
[name=张三, age=24, score=120.00]
[name=李四, age=23, score=110.00]
[name=李四, age=23, score=110.00]
[name=王五, age=25, score=140.00]
[name=王五, age=25, score=140.00]
=================================
[name=张三, age=24, score=120.00]
[name=张三, age=24, score=120.00]
[name=李四, age=23, score=110.00]
[name=李四, age=23, score=110.00]
[name=王五, age=25, score=140.00]
[name=王五, age=25, score=140.00]
=================================
[name=张三, age=24, score=120.00]
[name=张三, age=24, score=120.00]
[name=李四, age=23, score=110.00]
[name=李四, age=23, score=110.00]
[name=王五, age=25, score=140.00]
[name=王五, age=25, score=140.00]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.482 s
[INFO] Finished at: 2018-10-24T20:40:02+08:00
[INFO] ------------------------------------------------------------------------
我们来试试如何传递参数给 Main 类吧,先定义一个 Main 类:
package com.zfl9;
public class Main {
public static void main(String[] args) {
if (args.length == 0) {
System.err.println("missing arguments.");
System.exit(1);
}
System.out.println(java.util.Arrays.toString(args));
}
}
然后是修改后的 pom.xml 文件(因为我已经不需要 ognl 依赖包了):
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.zfl9</groupId>
<artifactId>javase</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<properties>
<maven.test.skip>true</maven.test.skip>
<exec.mainClass>com.zfl9.Main</exec.mainClass>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
我们先不传递任何参数试试,看看 Main 类是否正常运行:
# root @ arch in ~/maven-workspace/javase [20:46:08]
$ mvn clean package exec:java
[INFO] Scanning for projects...
[INFO]
[INFO] --------------------------< com.zfl9:javase >---------------------------
[INFO] Building javase 1.0
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ javase ---
[INFO] Deleting /root/maven-workspace/javase/target
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ javase ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /root/maven-workspace/javase/src/main/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ javase ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /root/maven-workspace/javase/target/classes
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ javase ---
[INFO] Not copying test resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ javase ---
[INFO] Not compiling test sources
[INFO]
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ javase ---
[INFO] Tests are skipped.
[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ javase ---
[INFO] Building jar: /root/maven-workspace/javase/target/javase-1.0.jar
[INFO]
[INFO] --- exec-maven-plugin:1.6.0:java (default-cli) @ javase ---
missing arguments.
提示 missing arguments
,没问题,那么我们传递参数试试:
# root @ arch in ~/maven-workspace/javase [20:46:48] C:1
$ mvn clean package exec:java -Dexec.args='www.zfl9.com'
[INFO] Scanning for projects...
[INFO]
[INFO] --------------------------< com.zfl9:javase >---------------------------
[INFO] Building javase 1.0
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ javase ---
[INFO] Deleting /root/maven-workspace/javase/target
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ javase ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /root/maven-workspace/javase/src/main/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ javase ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /root/maven-workspace/javase/target/classes
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ javase ---
[INFO] Not copying test resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ javase ---
[INFO] Not compiling test sources
[INFO]
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ javase ---
[INFO] Tests are skipped.
[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ javase ---
[INFO] Building jar: /root/maven-workspace/javase/target/javase-1.0.jar
[INFO]
[INFO] --- exec-maven-plugin:1.6.0:java (default-cli) @ javase ---
[www.zfl9.com]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.272 s
[INFO] Finished at: 2018-10-24T20:47:00+08:00
[INFO] ------------------------------------------------------------------------
没问题,正常显示,那么如果我们想传递转移序列呢?在之前我们可以使用 $''
来转移,那么现在呢?当然也可以啊,来我们试试:
# root @ arch in ~/maven-workspace/javase [20:47:44]
$ mvn exec:java -Dexec.args='www.zfl9.com\twww.baidu.com\twww.google.com'
[INFO] Scanning for projects...
[INFO]
[INFO] --------------------------< com.zfl9:javase >---------------------------
[INFO] Building javase 1.0
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- exec-maven-plugin:1.6.0:java (default-cli) @ javase ---
[www.zfl9.com\twww.baidu.com\twww.google.com]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.508 s
[INFO] Finished at: 2018-10-24T20:48:01+08:00
[INFO] ------------------------------------------------------------------------
# root @ arch in ~/maven-workspace/javase [20:48:01]
$ mvn exec:java -Dexec.args=$'www.zfl9.com\twww.baidu.com\twww.google.com'
[INFO] Scanning for projects...
[INFO]
[INFO] --------------------------< com.zfl9:javase >---------------------------
[INFO] Building javase 1.0
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- exec-maven-plugin:1.6.0:java (default-cli) @ javase ---
[www.zfl9.com www.baidu.com www.google.com]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.542 s
[INFO] Finished at: 2018-10-24T20:48:08+08:00
[INFO] ------------------------------------------------------------------------
看到了区别没?没加 $''
时,shell 并不会对字符串中的转移序列进行转义,而是原样输出,当我们添加了 $''
之后,就正常解析了。
OK,现在我们还需要介绍另一个知识点:依赖的作用范围(pom.xml 中的 <scope>
标签)。
compile
:编译范围,默认值。在 编译、测试、运行 3 种环境都有效。test
:测试范围,仅在 测试 环境中有效。provided
:提供范围,在 编译、测试 环境中有效。如 servlet-api
,运行时容器由提供。runtime
:运行范围,测试、运行 环境中有效。如 JDBC 驱动,只要能在运行前提供就行。system
:系统范围,项目所依赖的 jar 包不在 maven 本地仓库中,而是本机其他位置。一般来说,不建议使用 system 依赖范围,因为则会造成项目的不可移植。因为 system 依赖范围相当于你静态的指定了 jar 包的绝对路径,这就是硬编码了,绝对会造成项目的不可移植性。所以应尽量避免。
那么就剩下 compile、test、provided、runtime 四种了,test 很好理解,就是测试环境中会用到,就是在编译测试代码和运行测试代码时这种依赖才会被用到,其他时候不会被用到。那么 compile 范围也很好理解,则是默认值,也符合我们对依赖的一般定义,它就是从编译到运行期间都需要用到的依赖包,比如 ognl.jar 就是这种,编译的时候需要用到,运行时也需要用到(测试期间其实就是包括编译期、运行期)。而 provided 就是这个依赖包在运行时不需要,运行期间,对应的依赖会由容器提供(比如 java 应用服务器会提供 servlet-api,所以 servlet-api 就是 provided 范围),但是编译和测试期间(其实是测试的编译阶段)需要用到 servlet-api,不然 javac 编译不了啊。而 runtime 依赖范围和他有点相似(应该说相反),runtime 表示项目的对应依赖在编译期间(当然包括测试的编译期间)不需要用到,但是在实际运行的时候需要用到,比如 JDBC 的驱动实现,编译的时候我们只需要知道 JDBC-API 就行,则是 jdk 提供的,不需要额外的依赖,但是实际运行时是需要环境提供 JDBC 驱动的,不然无法运行。
关于 maven 的插件,我们先来介绍几个基本知识。
我们都知道 Maven 本质上是一个插件框架,它的核心并不执行任何具体的构建任务,所有这些任务都交给插件来完成,例如编译源代码是由 maven-compiler-plugin 插件完成的。进一步说,每个任务(这里所说的任务就是阶段,phase)对应了一个插件目标(goal),每个插件会有一个或者多个目标,例如 maven-compiler-plugin 的 compile 目标用来编译位于 src/main/java/ 目录下的主源码,testCompile 目标用来编译位于 src/test/java/ 目录下的测试源码。
用户可以通过两种方式调用 Maven 插件目标。第一种方式是将插件目标与生命周期的阶段(lifecycle phase)绑定,这样用户在命令行只是输入生命周期阶段而已,例如 Maven 默认将 maven-compiler-plugin 的 compile 目标与 default 生命周期的 compile 阶段绑定,因此命令 mvn compile
实际上是先定位到 compile 这一生命周期阶段,然后再根据绑定关系调用 maven-compiler-plugin 的 compile 目标。第二种方式是直接在命令行指定要执行的插件目标,例如 mvn archetype:generate
就表示调用 maven-archetype-plugin 插件的 generate 目标,这种带冒号的调用方式与生命周期无关(因此也不会触发 maven 各个阶段的依赖机制)。
认识上述 Maven 插件的基本概念能帮助你理解 Maven 的工作机制,不过要想更高效率地使用 Maven,了解一些常用的插件还是很有必要的,这可以帮助你避免一不小心重新发明轮子。多年来 Maven 社区积累了大量的经验,并随之形成了一个成熟的插件生态圈。Maven 官方有两个插件列表,第一个列表的 GroupId 为 org.apache.maven.plugins
,这里的插件最为成熟(官方维护的插件),具体地址为:http://maven.apache.org/plugins/index.html。第二个列表的 GroupId 为 org.codehaus.mojo
,这里的插件没有那么核心,但也有不少十分有用,其地址为:http://mojo.codehaus.org/plugins.html。
插件前缀 prefix
在前面的 maven 学习中,你经常会看到诸如 mvn <plugin-name>:<goal-name>
的使用方式,实际上这里的 plugin-name 应该改为 plugin-prefix
,即插件的前缀,比如 maven-compiler-plugin
插件的前缀是 compiler
,所以我们可以直接使用 mvn compiler:compile
来编译程序主代码,调用 mvn compiler:testCompile
来编译程序的测试代码。看到没,我们并没有写出 plugin 的坐标(基本坐标,三元组),但是 maven 好像知道我们哪个插件,这里面是有运作的机制呢?
其实就是插件的前缀在起作用,maven 为了简化使用,奉行“约定优于配置”原则,对于官方插件,groupId 的命名是有规则的,即 maven-${prefix}-plugin
,对应的默认 prefix 就是 ${prefix}
,所以我们可以知道,maven-compiler-plugin
插件的前缀就是 compiler
;而对于第三方插件(org.codehaus.mojo),groupId 的命名是这样的:${prefix}-maven-plugin
,对应的默认 prefix 就是 ${prefix}
,所以 exec-maven-plugin
插件的前缀就是 exec
(这个插件熟悉吧,基本是我们使用的第一个第三方插件,也是最常用的一个),这种约定优于配置的原则是的我们很容易的使用 maven 插件。
那你有没有想过,为什么 maven 要提出 plugin-prefix 这种概念呢?其实很简单,如果没有插件前缀,那么我们在 maven 命令行中如果想要调用某个插件的目标,必须将插件的坐标写出来,然后才能调用,比如不使用前缀调用 compiler:compile 目标,需要这么写:mvn org.apache.maven.plugins:maven-compiler-plugin:compile
,注意我们省略的 version 坐标。这样虽然能让 maven 很好的工作,但是却不是很好用,因为命令行太长的程序会让人产生负面情绪。这就是插件前缀的作用。
当然,除了使用 maven 的自动插件前缀映射机制外(约定优于配置),maven 也允许我们自己定义插件的前缀字符串,这个内容不属于入门知识,就不介绍了。默认的映射机制已经很好用了, 我觉得没有必要自找麻烦。
插件前缀还有一个好处就是,当我们在 maven 中调用一个不在本地仓库中的插件前缀时,maven 会自动去远程仓库中获取,而 maven 是如何知道我们使用的插件前缀的对应插件坐标的呢?当然是前面介绍的两个映射规则了。比如我们可以在 maven 命令行中这样调用:mvn eclipse:help
来获取 maven-eclipse-plugin 插件的帮助信息。
配置 pom.xml 中的插件
插件的配置比较简单,这是最常见的 pom.xml 插件的配置:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.zfl9</groupId>
<artifactId>javase</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<maven.test.skip>true</maven.test.skip>
<exec.mainClass>com.zfl9.Main</exec.mainClass>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.3</version>
<executions>
<execution>
<id>antrun-test</id>
<phase>pre-clean</phase>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
<configuration>
<tasks>
<echo>hello, world!</echo>
</tasks>
</configuration>
</plugin>
</plugins>
</build>
</project>
插件元素中的基本信息也是 maven 的坐标(三元组在 pom.xml 配置中经常出现,所以请牢记它们:groupId、artifactId、version),然后再常见点的就是 <configuration>
标签、<executions>
标签。configuration 很显然是用来配置插件的,而 executions 标签则是配置插件执行相关的(目标与阶段的绑定,等等)。注意,configuration 标签可以在 executions 标签外部,也可以在 executions 标签内部,前者为全局配置,后者为局部配置(针对不同的 goal),显然,局部配置的优先级要高于全局配置,否则局部配置还有意义么。比如上面的 antrun 的 configuration 元素就可以位于 executions 元素内部,如果同时存在,那么局部配置的优先级高。
这里告诉你一个小技巧,如果你不知道当前的 plugin 的版本号(不填会报错,但是要填的话又不知道具体版本号是多少),可以使用 mvn 命令行执行 mvn <plugin-prefix>:help
来查看对应插件的帮助信息,里面一般都有版本号的,比如 mvn antrun:help
就能看到 antrun 插件的版本号。
然后还有一个最佳实践就是,指定依赖和插件的版本号建议不要直接指定,而是将版本号放到 properties 元素内部,然后在需要用到的地方使用 ${prop-name}
来引用,这样是为了好后期维护,而不需要全文搜索替换版本号,这样太麻烦了,而且容易出错。
典型的 pom.xml
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.zfl9</groupId>
<artifactId>hello</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<name>hello world</name>
<url>https://www.zfl9.com</url>
<description>hello world application</description>
<properties>
<maven.test.skip>true</maven.test.skip>
<exec.mainClass>com.zfl9.Hello</exec.mainClass>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
maven 常用命令 [阶段]
mvn clean
:执行项目清理工作mvn validate
:检查项目信息是否正常mvn compile
:编译项目源代码和测试代码mvn test
:执行项目的测试(junit 单元测试)mvn package
:打包项目为 jar/war/ear 压缩包mvn verify
:对测试结果进行检查,确保满足质量要求mvn install
:将项目打包,然后安装到 maven 本地仓库mvn deploy
:将项目打包,然后部署到 maven 远程仓库共享mvn site
:生成项目的相关站点文档(一般领导喜欢看这个东西)mvn site-deploy
:将生成的相关站点文档部署到指定 WEB 服务器maven 常用命令 [其他]
mvn <plugin-prefix>:help
:查看指定插件的简要帮助mvn <plugin-prefix>:help -Ddetail
:插件指定插件的详细帮助mvn help:describe -Dplugin=<plugin-prefix>
:查看指定插件的简要帮助mvn help:describe -Dplugin=<plugin-prefix> -Ddetail
:查看指定插件的详细帮助maven 常用命令 [运行]
mvn exec:java -Dexec.mainClass=com.zfl9.Main
:运行项目(指定 Main 类) mvn exec:java -Dexec.mainClass=com.zfl9.Main -Dexec.args=args...
:运行项目(带参数)maven 常用命令 [帮助]
mvn help:system
:查看系统的环境变量和 java 属性mvn help:effective-pom
:查看当前项目的有效 pom.xmlmvn help:effective-settings
:查看当前 maven 的有效设置maven 阿里云镜像加速
修改 $MAVEN_HOME/conf/settings.xml
,在 mirrors 元素中添加:
<mirror>
<id>alimaven</id>
<mirrorOf>central</mirrorOf>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/repositories/central/</url>
</mirror>
maven 强制 jdk1.8 版本
修改 $MAVEN_HOME/conf/settings.xml
,在 profiles 元素中添加:
<profile>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</profile>
maven webapp 项目配置
<?xml version="1.0" encoding="UTF-8"?>
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.zfl9</groupId>
<artifactId>springmvc-learn</artifactId>
<version>1.0.0</version>
<packaging>war</packaging>
<properties>
<maven.test.skip>true</maven.test.skip>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
<configuration>
<!-- war 包名称 -->
<warName>ROOT</warName>
<!-- war 包目录 -->
<outputDirectory>/usr/local/tomcat/apps</outputDirectory>
<!-- 忽略 web.xml -->
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</build>
</project>