最近在阅读《Maven实战》这本书,分享阅读后对POM学习的心得。
由于本人学习搭建maven项目使用的IDE是idea,因此对于idea的mavenProject中Lifecycle中的选项进行如下说明:
- clean:清除产生的项目
- validate:验证工程是否正确,所有需要的资源是否可用
- compile:编译源代码
- test:运行测试
- package:打包
- verify:运行任何检查,验证包是否有效且达到质量标准
- install:将项目打包到本地Repository
- site:产生site
- deploy:上传到私服
如下所示,介绍了pom文件中常用标签的含义和用法:
<?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">
<!-- xml第一行 表示xml文档的版本和编码方式 -->
<!-- project是pom.xml的根元素。声明pom相关的命名空间及xsd元素,提供第三方工具(IDE中的XML编辑器)快速编辑POM -->
<!-- 当前POM模板的版本,对于MAVEN2以及MAVEN3,只能是4.0.0 -->
<modelVersion>4.0.0</modelVersion>
<!--
groupId、artifactId、version三个基本元素定义项目基本的坐标,
在Maven的世界,任何jar、pom或war都是基于这些基本的坐标进行区分
-->
<!--
groupId: 定义项目属于哪个组。命名规范:组织或公司存在关联。例如此处我取名为:com.mininglamp.hejs
artifactId: 定义当前MAVEN项目在组中唯一的ID,例如这里我取名为:study-maven
version: 指当前项目的版本。SNAPSHOT意为快照,说明项目处于开发中,是不稳定的版本,发布正式版删除SNAPSHOT
packaging: Maven的打包方式,不定义则默认是jar
-->
<groupId>com.mininglamp.hejs</groupId>
<artifactId>study-maven</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<!-- 声明项目名称,非必须,推荐给POM声明name,方便信息交流 -->
<name>Study Maven Project</name>
<!--
Maven继承,就是一定程度上消除重复
artifactId、groupId、version:指定父模块的坐标
relativePath:父模块POM的相对路径
-->
<!--
<parent>
<artifactId>父artifactId</artifactId>
<groupId>父groupId</groupId>
<version>父version</version>
<relativePath>父pom路径</relativePath>
</parent>
-->
<!--
Maven聚合,一个命令将多个项目同事构建
方法堆:包含各个模块之间继承与依赖关系,从而可以自动计算出来合理的模块构建顺序
-->
<modules>
<!-- 父子结构 -->
<!--
<module>项目名一</module>
<module>项目名二</module>
-->
<!-- 平行结构 -->
<!--
<module>../项目名一</module>
<module>../项目名二</module>
-->
</modules>
<!-- Maven属性配置,通过${}获取属性的值,例如:${junit.version}则表示4.12这个属性值 -->
<properties>
<junit.version>4.12</junit.version>
<spring.version>4.3.14.RELEASE</spring.version>
</properties>
<!-- dependencies 所有依赖范围 -->
<dependencies>
<!-- dependency 依赖的元素 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<!--
scope表示Maven的依赖范围,依赖范围compile、test、provided、runtime、system
compile:编译依赖范围,默认使用该依赖范围。编译、测试和运行的时候都需要使用该依赖
test:测试依赖范围,测试运行时候需要依赖
provided:已提供依赖。编译和测试有效,运行时无效。serlvet-api就是运行时候容器提供
runtime:运行时依赖范围。测试和运行时有效,编译时候无效
system:系统依赖范围。必须通过systemPath元素指定依赖文件的路径。
依赖不是通过maven仓库解析,与本机系统绑定紧密,因此移植性很差。可以引用通过环境变量。
import:导入依赖范围。该依赖范围不会对编译,测试,运行产生实际的影响。一般import指向type为pom
-->
<scope>test</scope>
</dependency>
<!--
Maven传递依赖性:引入的jar会依赖其他的jar包运行。
在spring4.0版引入spring-core的jar包,可以查看到spring-core.pom引入需要依赖的其他jar,当引入spring-corejar包的同事也会引入其依赖的jar
-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
<!--
optional元素表示maven的可选依赖,只会对当前项目B产生影响,对于其他项目依赖于B的时候,这两个依赖不会被传递
例如:项目A依赖于项目B的时候,则项目A中需要配置一个spring-core
-->
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
<!--
exclusions元素声明排除依赖,exclusions可以包含一个或者多个exclusion子元素,因此可以排除一个或者多个传递性依赖
声明exclusion的时候只需要groupId和artifactId,而不需要version元素,因为groupId和artifactId就能唯一定位依赖图中的某个依赖。
-->
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<!--
distributionManagement:表示将项目生成的构建部署到仓库中去。
前者repository表示发布版本的构建仓库
后者snapshotRepository表示快照版本的仓库
-->
<!--
<distributionManagement>
<repository>
<id>study-maven-releases</id>
<name>发型版本</name>
<url>发型版本的仓库地址</url>
</repository>
<snapshotRepository>
<id>study-maven-snapshot</id>
<name>快照版本</name>
<url>快照版本的仓库地址</url>
</snapshotRepository>
</distributionManagement>
-->
<!--
Maven安装文件自带中央仓库的配置,可以在$M2_HOME/lib/maven-model-builder-3,0.jar中找到一个pom,其中配置中央仓库信息
1. repositories 元素表示远程仓库配置,下面可以配置一个或者多个repository仓库信息
2. repository 元素表明一个具体的仓库信息
-->
<repositories>
<repository>
<!-- 任何一个repository的id必须是唯一的,尤其需要注意的Maven自带中央仓库使用的是id为central,
若果其他仓库id声明也是central,则覆盖中央仓库的配置 -->
<id>central</id>
<name>Maven Repository Switchboard</name>
<url>http://repo1.maven.org/maven2/</url>
<!-- layout配置default表示仓库的布局默认是Maven2及Maven3的默认布局,而不是Maven1的布局 -->
<layout>default</layout>
<!-- releases配置enabled为true,表示不从该中央仓库下载发布版本的构件 -->
<releases><enabled>true</enabled></releases>
<snapshots>
<!-- snapshots配置enabled为false,表示不从该中央仓库下载快照版本的构件 -->
<enabled>false</enabled>
<!--
updatePolicy元素配置Maven从远程仓库检查更新的频率,默认是daily
daily:表示Maven每天检查一次
never:从不检查更新
always:每次构建都检查更新
interval:X-每个X分钟检查一次更新(X为任意整数)
-->
<updatePolicy>daily</updatePolicy>
<!--
checksumPolicy配置Maven检查校验和文件的策略,默认配置warn
warn:Maven会在执行构建是输出警告信息
fail-Maven:遇到校验和错误就让构建失败
ignore:Maven完全忽略校验和错误
-->
<checksumPolicy>warn</checksumPolicy>
</snapshots>
</repository>
</repositories>
<!-- Maven构建 -->
<build>
<plugins>
<!-- maven的默认编译使用的jdk版本貌似很低,使用maven-compiler-plugin插件可以指定项目源码的jdk版本,编译后的jdk版本,以及编码。
指定maven编译的jdk版本,如果不指定,maven3默认用jdk 1.5,maven2默认用jdk1.3 -->
<plugin>
<!-- 插件存在默认groupId,可以不写,但是不方便团队人员理解 -->
<!--<groupId>org.apache.maven.plugins</groupId>-->
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<!-- 一般而言,target与source是保持一致的,但是,有时候为了让程序能在其他版本的jdk中运行(对于低版本目标jdk,
源代码中不能使用低版本jdk中不支持的语法),会存在target不同于source的情况 -->
<source>1.9</source> <!-- 源代码使用的JDK版本 -->
<target>1.9</target> <!-- 需要生成的目标class文件的编译版本 -->
<encoding>UTF-8</encoding> <!-- 字符集编码 -->
</configuration>
</plugin>
<!-- 测试运行器 -->
<!-- 自动执行测试源代码路径(默认为/src/test/java/)下符合一组命名模式的测试类:
**/Test*.java 任何子目录下所有命名以Test开头的Java类;
**/*Test.java 任何子目录下所有命名以Test结尾的Java类;
**/*TestCase.java 任何子目录下所有命名以TestCase结尾的Java类;
-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
</plugin>
<!--
maven-jar-plugin: maven 默认打包插件,用来创建 project jar
maven-shade-plugin: 用来打可执行包,包含依赖,以及对依赖进行取舍过滤
maven-assembly-plugin: 支持定制化打包方式,更多是对项目目录的重新组装
-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>1.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation = "org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.mininglamp.hejs.studyMaven.StudyMaven</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
**
Maven依赖范围和传递性依赖
**
1. Maven依赖范围
各种依赖范围和三种classpath的关系如下表:
| 依赖范围(SCOPE)| 编译classpath有效 | 测试classpath有效 | 运行classpath有效 | 例子 |
| ------------- |:-------------? -----?
| compile| Y | Y | Y | spring-core |
| test| - | Y | - | JUnit |
| provided | Y | Y | - | serlvet-api |
| runtime | - | Y | Y | JDBC驱动实现 |
| system | Y | Y | - | 本地的,Maven仓库以外的类库文件 |
2. Maven传递性依赖
引入的jar需要依赖其他的jar包进行运行,maven会自动下载jar所需的jar包
3. 两者之间的关系
假设A依赖于B,B依赖于C,我们说A对于B是第一直接依赖,B对于C是第二直接依赖,A对于C是传递性依赖。第一直接依赖的范围和第二直接依赖的范围决定了传递性依赖的范围。如下表所示:
| 第一直接依赖\第二直接依赖 | compile | test | provided | runtime |
| ------------- |:-------------? -----?
| compile| compile| - | - | runtime |
| test| test | - | - | test |
| provided | provided | - | provided | provided |
| runtime | runtime | - | - | runtime |
由上表可得出如下结论:
- 当第二直接依赖的范围是compile的时候,传递性依赖范围与第一直接依赖的范围一致。
- 当第二直接依赖的范围是test的时候,依赖不会进行传递。
- 当第二直接依赖的范围是provided的时候,只传递第一直接依赖范围是provided的依赖,且传递性依赖范围同为provided
- 当第二直接依赖的范围是runtime的时候,传递性依赖的范围与第一直接依赖的范围一致,但compile例外,此时传递性依赖的范围是runtime