这里记录 maven 的基础知识,一方面坚固本人学到的常识,另一方面心愿能够对有同样困惑的小伙伴提供一些帮忙。
一. maven 简介
Maven
是一个项目管理工具,它蕴含了一个我的项目对象模型 POM
(Project Object Model),一组规范汇合,一个我的项目生命周期(Project Lifecycle),一个依赖管理系统(Dependency Management System) 和用来运行定义在生命周期阶段 (phase) 中插件 (plugin) 指标 (goal) 的逻辑。当你应用 Maven 的时候,你用一个明确定义的 POM
来形容你的我的项目,而后 Maven
能够利用横切的逻辑,这些逻辑来自一组共享的(或者自定义的)插件。
Maven 的外围其实不做什么理论的事件,除了解析一些 XML 文档,治理生命周期与插件之外,它什么也不懂。Maven 被设计成将次要的职责委派给一组 Maven 插件,这些插件能够影响 Maven 生命周期,提供对指标的拜访。绝大多数 Maven 的动作产生于 Maven 插件的指标(goal),如编译源码,打包二进制代码,公布站点和其它构建工作。你从 Apache 下载的 Maven 不晓得如何打包 WAR 文件,也不晓得如何运行单元测试,Maven 大部分的智能是由插件实现的,而插件从 Maven 仓库取得。事实上,第一次你用全新的 Maven 装置运行诸如 mvn install
命令的时候,它会从地方 Maven 仓库下载大部分外围 Maven 插件。这不仅仅是一个最小化 Maven 散发包大小的技巧,这种形式更能让你降级插件以给你我的项目的构建进步能力。Maven 从近程仓库获取依赖和插件的这一事实容许了构建逻辑的全局性重用。
应用上面的命令能够查看指定插件的详细信息:
mvn help:describe -Dplugin=install -Dfull
二. maven 生命周期
1. maven 默认生命周期
生命周期阶段 | 形容 |
---|---|
validate | 验证我的项目是否正确,以及所有为了残缺构建必要的信息是否可用 |
generate-sources | 生成所有须要蕴含在编译过程中的源代码 |
process-sources | 解决源代码,比方过滤一些值 |
generate-resources | 生成所有须要蕴含在打包中的资源文件 |
process-resources | 复制并解决资源文件至指标 (target) 目录,筹备打包 |
compile | 编译我的项目的源代码 |
process-classes | 后处理编译生成的 class 文件,例如对 Java 类进行字节码加强 |
generate-test-sources | 生成所有蕴含在测试编译过程中的测试源码 |
process-test-sources | 解决测试源码,比方过滤一些值 |
generate-test-resources | 生成测试须要的资源文件 |
process-test-resources | 复制并解决测试资源文件至测试目标目录 |
test-compile | 编译测试源码至测试目标目录 |
test | 应用适合的单元测试框架运行测试,这些测试不须要被打包或公布 |
prepare-package | 在真正的打包之前,执行一些筹备打包必要的操作 |
package | 将编译好的代码打包成可散发的格局,如 JAR,WAR |
pre-integration-test | 执行一些在集成测试运行之前须要的动作。如建设集成测试须要的环境 |
integration-test | 如果有必要的话,解决包并公布至集成测试能够运行的环境 |
post-integration-test | 执行一些在集成测试运行之后须要的动作。如清理集成测试环境。 |
verify | 执行所有查看,验证包是无效的,合乎品质标准 |
install | 安装包至本地仓库,以备本地的其它我的项目作为依赖应用 |
deploy | 安装包至近程仓库,共享给其余开发人员和我的项目 |
在下面的表格中标出了罕用的生命周期,对应 idea 中 maven 的生命周期如下图:
2. 执行 mvn package 产生了什么
咱们能够将插件的指标绑定到 maven 的生命周期阶段上,执行 mvn package
命令,在 Maven 沿着生命周期一步步向前的过程中,它将运行绑定在每个阶段上的所有指标。
三. maven 我的项目依赖
Maven 能够治理外部和内部依赖。外部依赖是指一个 maven 我的项目外部各个模块之间的依赖,比方一个三层架构的 web 我的项目中 service 模块依赖 model 模块,或者长久层模块;内部依赖是指依赖第三方类库,比方依赖 spring、mybatis 等相干的依赖。
1. 依赖范畴
在 pom.xml
文件中指定 dependency
依赖时,能够指定依赖的 scope
范畴,范畴能够管制哪些依赖在哪些classpath
中可用,哪些依赖蕴含在一个利用中。让咱们具体看一下每一种范畴:
-
compile(编译范畴)
compile 是默认的范畴。如果没有提供一个范畴,那该依赖的范畴就是编译范畴。编译范畴依赖在所有的
classpath
中可用,同时它们也会被打包。 -
provided(已提供范畴)
provided
依赖只有在当JDK
或者一个容器已提供该依赖之后才应用。例如,如果你开发了一个 web 利用,你可能在编译classpath
中须要可用的Servlet API
来编译一个servlet
,然而你不会想要在打包好的 WAR 中蕴含这个Servlet API
,这个Servlet API JAR
由你的应用服务器或者servlet
容器提供。 已提供范畴的依赖在编译classpath
(不是运行时)可用,它们不是传递性的,也不会被打包。 -
runtime(运行时范畴)
runtime
依赖在运行的时候须要,但在编译的时候不须要。比方,你可能在编译的时候只须要JDBC API JAR
接口,而只有在运行的时候才须要JDBC
驱动实现。 -
test(测试范畴)
test 范畴依赖在个别的编译和运行时都不须要,它们只有在 测试编译和测试运行阶段可用。
-
system(零碎范畴)
system 范畴依赖与 provided 相似,然而你必须显式的提供一个对于本地零碎中 JAR 文件的门路。这么做是为了容许基于本地对象编译,而这些对象是零碎类库的一部分。这样的构件应该是始终可用的,Maven 也不会在仓库中去寻找它。如果你将一个依赖范畴设置成零碎范畴,你必须同时提供一个 systemPath 元素。留神该范畴是不举荐应用的(你应该始终尽量去从公共或定制的 Maven 仓库中援用依赖)。
scope 取值 无效范畴(compile, runtime, test) 依赖传递 例子 compile(默认) all 是 spring-core provided compile、test 否 servlet-api runtime runtime、test 是 JDBC 驱动 test test 否 Junit system compile、test 是 不举荐应用
2. 可选依赖
在 pom.xml 文件中指定 <dependency>
依赖时,能够通过 <optional>true<optional>
标签将此依赖申明为可选依赖。如果我的项目中依赖的类库蕴含可选依赖,咱们在应用的时候须要显示指定须要的可选依赖。
3. 传递依赖
传递性依赖就是对于一个依赖的依赖。如果 project-a 依赖于 project-b,而后者接着依赖于 project-c,那么 project-c 就被认为是 project-a 的传递性依赖。如果 project-c 依赖于 project-d,那么 project-d 就也被认为是 project-a 的传递性依赖。Maven 的局部劣势是因为它可能治理传递性依赖,并且可能帮忙开发者屏蔽掉跟踪所有编译期和运行期依赖的细节。你能够只依赖于一些包如 Spring Framework,而不必放心 Spring Framework 的所有依赖,Maven 帮你主动治理了,你不必本人去具体理解配置。
Maven 是怎么实现这件事件的呢?它建设一个依赖图,并且解决一些可能产生的抵触和重叠。如果 Maven 看到有两个我的项目依赖于同样的 groupId 和 artifactId,它会主动抉择那个最新版本的依赖。尽管这听起来很不便,但在一些边界状况中,传递性依赖会造成一些配置问题。在这种状况下,你能够应用 <exclusion>
依赖排除。
上面几种状况能够应用 <exclusion>
依赖排除:
- 排除通过传递依赖引入然而我的项目中没有应用的依赖
- 排除运行时容器曾经提供的依赖
- 排除可能有多个实现的 API 依赖
- 有多个版本的依赖,排除你不想应用的那个依赖
4. 依赖治理
在一个有很多模块的简单 maven 我的项目中,如果有很多模块都应用了雷同的依赖,比方 MySQL
数据库驱动的依赖,须要在各个模块中独立的列出该依赖的版本,在你须要降级到一个新版本的时候你就会遇到问题。因为这些版本号扩散在你的我的项目树中,你须要手动批改每一个援用该依赖的 pom.xml
,确保每个中央的版本号都更改了。那么如何解决这个问题呢?
maven 提供了 dependencyManagement
元素来对立治理依赖版本号,应用 pom.xml
中的dependencyManagement
元素能让你在子项目中援用一个依赖而不必显式的列出版本号,maven 会沿着父子档次向上走,直到找到一个领有 dependencyManagement
元素的我的项目,而后它就会应用在这个
dependencyManagement
元素中指定的版本号。
留神 如果子项目定义了一个版本
version
,它将笼罩顶层 POM 的dependencyManagement
元素中的版本。只有在子项目没有间接申明一个版本的时候,dependencyManagement
定义的版本才会被应用。
定义在顶层 pom.xml
文件的 dependencyManagement
中的依赖,在子项目中如果没有显示援用则不会被引入,然而定义顶层 pom.xml
的 dependencies
中的依赖会被所有子项目引入,为了不增加一些不必要的依赖,应用 dependencyManagement
能让你对立并集中化依赖版本的治理,而不必增加那些会被所有子项目继承的依赖。换句话说,dependencyManagement
元素和一个环境变量一样,能让你在一个我的项目上面的任何中央申明一个依赖而不必指定一个版本号。
四. maven 内置属性
1. maven 属性
你能够在 pom.xml
文件或者资源文件中应用属性,资源文件被会 Maven Resource
插件的过滤个性解决。一个属性永远蕴含在中 ${}
中。例如,要援用 project.version
属性,就须要这样写 ${project.version}
。在任何 Maven 我的项目中都有一些隐式的属性,这些隐式的属性是:
-
project.*
能够应用该
project.*
前缀来援用任何在pom.xml
中的值。 -
settings.*
能够应用该
settings.*
前缀来援用~/.m2/settings.xml
文件中的值。 -
env.*
能够应用
env.*
前缀来援用环境变量如PATH
和M2_HOME
的值。 -
零碎属性
任何能够通过
System.getProperty()
办法获取的属性都能够作为 maven 属性被援用。
能够应用 mvn help:system
命令可查看所有环境变量。
除了上述的隐式属性,用户还能够应用 <properties>
自定义属性。
2. pom.xml 文件罕用属性
${project.groupId}: 我的项目的 groupId.
${project.artifactId}: 我的项目的 artifactId.
${project.version}: 我的项目的 version, 于 ${version}等价
${project.basedir}: 示意我的项目根目录,即蕴含 pom.xml 文件的目录;
${project.build.sourceDirectory}: 我的项目的主源码目录,默认为 src/main/java/.
${project.build.testSourceDirectory}: 我的项目的测试源码目录,默认为 /src/test/java/.
${project.build.directory}: 我的项目构建输入目录,默认为 target/.
${project.outputDirectory}: 我的项目主代码编译输入目录,默认为 target/classes/.
${project.testOutputDirectory}: 我的项目测试代码编译输入目录,默认为 target/testclasses/.
${project.build.finalName}: 我的项目打包输入文件的名称,默认为 ${project.artifactId}-${project.version}
3. setting.xml 文件属性
与 pom 属性同理,用户能够用以 settings.
结尾的属性援用 setting.xml
文件的 XML 元素值。
${settings.localRepository}
示意本地仓库的地址
4. 自定义属性
自定义属性:在 pom.xml
文件的 <properties>
标签下定义的 maven 属性:
<properties>
<spring>5.2.13.RELEASE</spring>
</properties>
能够在其余中央应用自定义的属性:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring}</version>
</dependency>
五. POM 最佳实际
1. 依赖归类
如果你有一组逻辑上归类在一起的依赖,你能够创立一个打包形式为 pom
的子模块来将这些依赖归在一起。比方创立一个叫做 dependencies
的子模块,此模块只蕴含一个 pom.xml
文件,用来归类治理一些公共依赖的版本号,留神指定此模块的打包类型为:<packaging>pom</packaging>
。
比方在 dubbo 我的项目中也是这样应用的,它定义了一个叫做 dubbo-dependencies-bom
的子模块,模块中只有一个 pom.xml
文件,如下图:
在 dubbo-dependencies-bom
的父 pom.xml
中通过如下形式引入应用:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-dependencies-bom</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2. 资源过滤的问题
在下面的 maven 生命周期一节咱们能够晓得,maven 将 resources:resources
指标绑定到 process-resources
生命周期阶段,默认状况下 resources:resources
指标的作用是将 src/main/resources
目录下的文件复制到 target/classes
目录中。除了复制文件到输入目录之外,resources:resources
指标还能够应用过滤器,用于将资源文件中的符号变量(${变量名}
)替换为变量指向的值,能够应用上面的形式开启过滤器性能:
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<!-- 应用 <excludes> 排除 src/main/resources 目录下的 test.txt 文件 -->
<excludes>
<exclude>test.txt</exclude>
</excludes>
<!--
开启过滤器性能,开启之后就能够从 <properties> 元素
或者上面 <filters> 中指定的 properties 文件中读取变量的值
替换 ${变量名}
-->
<filtering>true</filtering>
</resource>
</resources>
<filters>
<filter>src/main/resources/my.properties</filter>
</filters>
</build>
能够应用 <excludes>
排除指定目录下的指定文件,反对通配符匹配,比方通过 *.txt
排除以 .txt
为后缀的文件。
通过 <filtering>true</filtering>
开启过滤器性能,开启之后就能够从 pom.xml
文件、<properties>
元素或者 <filters>
中指定的 properties 文件中读取变量的值替换 ${变量名}
。
须要留神的是:
在
pom.xml
文件、<properties>
元素或者<filters>
中有雷同变量名时,读取变量优先级如下:
<filters>
><properties>
>pom.xml
上面通过一个例子来阐明下面的性能,定义一个 hello.txt
文件,文件中通过 ${变量名}
形式定义一些变量,用来验证变量替换的性能,定义了 test.txt
用来验证文件排除性能,定义了一个 my.properties
文件指定变量的值,如下图所示:
应用如下命令编译打包:
mvn clean package
输入后果如下图:
从后果中咱们能够看出,test.txt
文件被排除了,自定义变量的值也被正确替换了。
资源文件是 Java 代码中要应用的文件,代码在执行的时候会到指定目录去查找这些文件。个别状况下,咱们用到的资源文件(各种 xml,properties,xsd 文件等)都放在 src/mian/resources
下,利用 Maven 打包的时候,Maven 都能默认将这些资源文件打包到相应的 jar 包或者 war 包中。
然而有些时候,可能会把资源文件放到 src/main/java
下,比如说 Mybatis
的Mapper.xml
文件就是和 Mapper.java
放在一起,也就导致了 java 文件能打到 jar 包中,Mapper.xml 却没有打进 jar 包中。这时候,就须要在 pom.xml 中增加特定的配置来将这些资源文件能打进 jar 包或 war 包中。
配置有两种办法,一是在 <build>
标签节点下增加 <resources>
标签节点,二是在 <build>
标签节点下的 <plugins>
标签节点中配置 maven-resources-plugin
等解决资源文件的插件。
<resources>
标签形式引入src/main/java
中的properties
和xml
文件
<build>
.......
<resources>
<resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>**/*.properties</exclude>
<exclude>**/*.xml</exclude>
</excludes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
......
</build>
maven-resources-plugin
插件形式引入src/main/java
中的properties
和xml
文件
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>2.5</version>
<executions>
<execution>
<id>copy-xmls</id>
<phase>process-sources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${basedir}/target/classes</outputDirectory>
<resources>
<resource>
<directory>${basedir}/src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
参考文档
maven 指南