第二章节开始就是满满的干货,本章介绍pom文件中的内容。知识点分为三个
- pom文件的总体结构
- super pom
- 项目, 自身的标识,关系等。
1、pom文件
pom文件的总体结构很复杂,但是经常用到的比较少。它大致分为以下几个部分
- 第一部分为与项目有关的信息,它的开发者,组织机构,认证license等等
- 第二部分为项目的依赖。
- 第三部分为项目的编译过程。
- 第四部分为依赖的仓库以及依赖插件的仓库
- 第五部分为其他杂项。这些很少用到
1.1 自身信息
项目的相关信息包含项目自身的信息,项目的开发人员的相关信息,项目的其他信息。
项目自身的信息
<!-- 项目的名称 --> <name>childB</name> <!-- 项目的描述--> <description>childB project for learn pom file constructor </description> <!-- 项目的url地址 --> <url>http://www.example.com</url> <!-- 项目的组织机构Id --> <groupId>com.learn.maven</groupId> <!-- 项目的jar包Id --> <artifactId>childB</artifactId> <!-- 项目的版本号 --> <version>1.0-SNAPSHOT</version> <!-- 项目的打包形式,支持war,jar等等 --> <packaging>jar</packaging>
- name标签用户指定项目的名称,在创建项目时自动生成
- description标签用于指定项目的描述信息。
- url标签用于指定项目的url地址
- groupId标签用于指定项目的组织机构ID,一般是域名的逆序。它是必填的。
- artifactId标签用于指定项目的ID。它是必填的
- version标签用于指定项目的版本,它是必填的。
- packaging标签用于指定项目打包的形式,经常用到的有pom, war, jar。它是必填的
项目开发者的信息
<!-- 项目的开发相关信息 --> <!-- 项目的组织机构信息 --> <organization> <name>com.learn.someBody</name> <url>http://www.example.com</url> </organization> <!-- 项目的开发人员信息 --> <developers> <developer> <name>Jack</name> <email>XXX@126.com</email> </developer> <developer> <name>Mike</name> <email>XXX@126.com</email> </developer> </developers> <!-- 项目的卓越贡献者--> <contributors> <contributor> <name>somebody</name> <email>XXX@126.com</email> </contributor> </contributors>
- organization标签用于指定项目的开发机构
- developers标签用于指定项目的开发者
- contributors标签用于指定项目的共享者
项目的其他信息
- parent标签用于指定项目的父项目。它属于项目的关系,当建立父子关系项目时,必填。
- modelVersion标签用于指定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/xsd/maven-4.0.0.xsd">
3.licenses标签用于指定项目的认证信息。
1.2 依赖
项目的依赖即项目之间的关系。有两种类型,一种是继承,一种是组合关系。
继承关系
- 若是父项目,使用modules标签指定子项目
<modules> <module>childA</module> <module>childB</module> </modules>
2.若是子项目,使用parent标签指定父项目。
<parent> <artifactId>parent</artifactId> <groupId>org.example</groupId> <version>1.0-SNAPSHOT</version> </parent>
组合关系
- Dependencies标签添加一到多个依赖。经常见,略。
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> </dependencies>
2.dependencyManagement标签用于在父项目中定义子项目需要继承的依赖。
<!-- 覆盖父项目中的一到多个依赖 --> <dependencyManagement> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> <scope>test</scope> </dependency> </dependencies> </dependencyManagement>
1.3 编译
1.3.1 conversion(约定俗成)
约定俗成的意思是指项目目录结构约定俗成的规则。
约定俗成是指主要代码应该放在src/main下面,测试代码应该放在src/test下面。脚本应该放在src/main/scripts下面。
约定俗成在super pom中有所体现
<!-- 项目的默认编译路径,项目当前路径/target--> <directory>${project.basedir}/target</directory> <!-- src/main/java,src/main/resources编译的存放路径--> <outputDirectory>${project.build.directory}/classes</outputDirectory> <!-- 项目打包的默认名称 --> <finalName>${project.artifactId}-${project.version}</finalName> <!-- src/test/java,src/test/resources编译的存放路径 --> <testOutputDirectory>${project.build.directory}/test-classes</testOutputDirectory> <!-- 资源的存放路径 --> <sourceDirectory>${project.basedir}/src/main/java</sourceDirectory> <!-- 脚本的存放路径 --> <scriptSourceDirectory>${project.basedir}/src/main/scripts</scriptSourceDirectory> <!-- 测试的存放路径 --> <testSourceDirectory>${project.basedir}/src/test/java</testSourceDirectory> <!-- 配置资源的定义,包含src/main/resources--> <resources> <resource> <directory>${project.basedir}/src/main/resources</directory> </resource> </resources> <!-- 测试配置资源的定义,包含src/main/resources--> <testResources> <testResource> <directory>${project.basedir}/src/test/resources</directory> </testResource> </testResources>
1.3.2 Include & exclude规则
默认情况下, 编译会包含src/main下面所有的资源。可以通过resources指定更小的范围,它的结构为
<resources> <resource> <includes></includes> <excludes></excludes> <directory></directory> <filtering>true</filtering> <targetPath>/target</targetPath> </resource> </resources>
Resources包含一到多个resource,指定资源的规则,
- includes指定包含的资源,例如只想编译特定包下面的资源
- excludes指定排除的资源。
- directory指定资源的存放目录
- filtering指定是否经过过滤。
- targetPath指定编译输出的目录。
1.3.3 打包
配置finalName标签,指定项目最终打包的名称。
1.3.4 插件
TODO。
1.4 仓库地址
项目的仓库有两类,一种是依赖jar包的仓库地址,另外一种是依赖插件的仓库地址。
- repositories标签用于设置依赖的仓库地址,例如添加阿里的仓库地址
<!-- 仓库地址 --> <repositories> <repository> <id>nexus-aliyun</id> <name>Nexus aliyun</name> <layout>default</layout> <url>http://maven.aliyun.com/nexus/content/groups/public</url> <snapshots> <enabled>false</enabled> </snapshots> <releases> <enabled>true</enabled> <checksumPolicy>ignore</checksumPolicy> <!-- 每天检查,自动更新jar包,在实际中不要设置 --> <updatePolicy>daily</updatePolicy> </releases> </repository> </repositories>
2.pluginRepositories标签用于设置插件的仓库地址,它们基本上是同一个
1.5 报告
报告是项目生命周期的尾声。它的结构格式如下:
<reporting> <plugins></plugins> <excludeDefaults>true</excludeDefaults> <outputDirectory></outputDirectory> </reporting>
- Plugins:用于指定生成报告的插件,通常是对应site生命周期,关联默认的插件,无需特别指定。
- excludeDefaults:生成的报告中是否包含一些默认的信息,例如项目自身的信息。
- outputDirectory:报告生成之后,指定它们的存放路径。
1.6 profile
TODO
1.7 distributionManagement
TODO
1.8 其他
issueManagement用于管理项目问题追踪,类似于bug平台。
<issueManagement> <!-- 系统的url地址 --> <url></url> <!-- 系统的名称 --> <system></system> </issueManagement>
ciManagement用于项目持续更新迭代的地址,ci的全称为continous intergrate。
<ciManagement> <!-- 持续集成系统的名称 --> <system></system> <!-- 持续集成系统的url地址 --> <url></url> <!-- 通知的配置 --> <notifiers> <notifier> <!-- 通知的类型 --> <type>mail</type> <!-- 错误时是否发送通知 --> <sendOnError>true</sendOnError> <!-- 异常时是否发送通知 --> <sendOnFailure>true</sendOnFailure> <!-- 正常时是否发送通知 --> <sendOnSuccess>false</sendOnSuccess> <!-- 警告时是否发送通知 --> <sendOnWarning>false</sendOnWarning> <!-- 通知的地址 --> <address>xx@126.com</address> </notifier> </notifiers> </ciManagement>
mailingList用于指定公开的邮箱列表,主要用于提交使用者的反馈。
<mailingLists> <mailingList> <!-- 邮件的名称 --> <name></name> <!-- 订阅的邮件地址 --> <subscribe></subscribe> <!-- 不订阅的邮件地址 --> <unsubscribe></unsubscribe> <!-- 项目每次迭代生成Jar包的url地址 --> <archive></archive> <!-- 其他可供选择的url地址 --> <otherArchives></otherArchives> <!-- 当需要反馈时,用户将反馈内容发送到该地址 --> <post></post> </mailingList> </mailingLists>
scm用于指定软件配置管理的功能,全称为software configuration management。类似git, svn之类的系统。
2、super pom
pom本质也是一种树形结构,super pom就是树的根。类似于Java中的Object。有效的pom,包含所有父pom以及子pom中存在的项。
在日常开发中,若没有将项目模块化,项目的pom会直接继承super pom。
Super pom存在于maven-model-builder-version.jar中,路径为org/apache/maven/model。它的名称为pom-modelVersion.xml。目前使用的modelVersion为4.0.0。
下面详细介绍super pom的内容
2.1 自身信息
项目相关信息中,公共部分极少,在super pom中只有modelVersion。源配置如下:
<modelVersion>4.0.0</modelVersion>
2.2 依赖
无
2.3 仓库地址
仓库地址有两类,依赖的maven仓库地址,插件的maven仓库地址
依赖的maven仓库地址, 它默认的仓库地址是maven2,不下载快照版本的依赖
<repositories> <repository> <id>central</id> <name>Central Repository</name> <url>https://repo.maven.apache.org/maven2</url> <layout>default</layout> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories>
插件的maven仓库地址,它默认也是maven2,不下载快照版本,不自动更新
<pluginRepositories> <pluginRepository> <id>central</id> <name>Central Repository</name> <url>https://repo.maven.apache.org/maven2</url> <layout>default</layout> <snapshots> <enabled>false</enabled> </snapshots> <releases> <updatePolicy>never</updatePolicy> </releases> </pluginRepository> </pluginRepositories>
在这里若要添加阿里云的仓库,可以直接添加到super pom当中。并替换原来jar包中的文件。
2.4 编译
编译的内容分为两个部分,编译的路径,编译使用的插件。
2.4.1 路径
<!-- 项目的默认编译路径,项目当前路径/target--> <directory>${project.basedir}/target</directory> <!-- src/main/java,src/main/resources编译的存放路径--> <outputDirectory>${project.build.directory}/classes</outputDirectory> <!-- 项目打包的默认名称 --> <finalName>${project.artifactId}-${project.version}</finalName> <!-- src/test/java,src/test/resources编译的存放路径 --> <testOutputDirectory>${project.build.directory}/test-classes</testOutputDirectory> <!-- 资源的存放路径 --> <sourceDirectory>${project.basedir}/src/main/java</sourceDirectory> <!-- 脚本的存放路径 --> <scriptSourceDirectory>${project.basedir}/src/main/scripts</scriptSourceDirectory> <!-- 测试的存放路径 --> <testSourceDirectory>${project.basedir}/src/test/java</testSourceDirectory> <!-- 配置资源的定义,包含src/main/resources--> <resources> <resource> <directory>${project.basedir}/src/main/resources</directory> </resource> </resources> <!-- 测试配置资源的定义,包含src/main/resources--> <testResources> <testResource> <directory>${project.basedir}/src/test/resources</directory> </testResource> </testResources>
2.4.2 插件
<pluginManagement> <!-- NOTE: These plugins will be removed from future versions of the super POM --> <!-- They are kept for the moment as they are very unlikely to conflict with lifecycle mappings (MNG-4453) --> <plugins> <plugin> <artifactId>maven-antrun-plugin</artifactId> <version>1.3</version> </plugin> <plugin> <artifactId>maven-assembly-plugin</artifactId> <version>2.2-beta-5</version> </plugin> <plugin> <artifactId>maven-dependency-plugin</artifactId> <version>2.8</version> </plugin> <plugin> <artifactId>maven-release-plugin</artifactId> <version>2.5.3</version> </plugin> </plugins> </pluginManagement>
Super pom中定义了一些插件的版本,注释部分中看到这些配置项将来会被移除。
2.5 报告
调用site插件,可以生成项目的报告,在实际中,较少使用。它的源码如下:
<reporting> <outputDirectory>${project.build.directory}/site</outputDirectory> </reporting>
指定生成报告的路径。
2.6 profile
源码中可以看到,在将来会被移除。所以本部分内容略。
<profiles> <!-- NOTE: The release profile will be removed from future versions of the super POM -->
3、项目
项目的知识点包含两个,项目自身,项目的关系。
3.1 坐标
一个机构可以开发多个项目,每个项目都有多个版本,每个版本都有不同的打包形式。
项目的坐标就是由上述的几个核心信息构成的。
groupId标签用于指定项目的机构信息
artifactId标签用于指定项目的标识
version标签用户指定项目的版本号,其中SNAPSHOT为快照版本,意思是正在开发中。release为发布版本,意思是已经开发完成,并且发布的稳定版本。
packaging标签用于指定项目的打包形式,pom,war, jar。其中pom表示为抽象的maven项目,它通常是一个依赖的集合,或者是父项目。
3.2 关系
项目之间的关系有两种,继承关系和组合关系。
继承关系主要是指父项目中包含多个子项目,要建立多层次的父子关系,可以使子项目同时承担父项目和子项目的角色。例如parent项目下包含childA,childB项目,childA项目又包含subchildA1, subchildA2等项目。
它可以无限层级的嵌套,但是最好保持易用性。
组合关系主要是指项目A依赖项目B,项目B又依赖项目C,是一种不闭环的链。若存在闭环时,会导致失败。例如A依赖B,B依赖C,C又依赖A。
在管理依赖时,这两种方式都非常方便。
继承时,子项目可以复用父项目的依赖,在引入时,可以省略版本信息。例如
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> </dependency>
此处父项目中存在junit依赖,所以子项目可以省略版本号。可以把子项目公共的依赖放入父项目中。这样会非常方便。不过使用继承的大部分场景是模块化开发,即把项目拆分成多个小的项目,它们彼此之间相互依赖,但是可以同时并行开发。这符合当今主流的微服务架构。
组合时,子项目可以添加项目依赖,或pom型依赖。项目依赖很常见,例如添加junit,logback,spring等依赖。它的缺点是需要自己独立的管理依赖的版本号,以及防止传递性依赖导致的冲突,传递性依赖指A依赖B,B依赖C,C依赖D等等,在Idea中可以使用maven helper分析传递性依赖。
当有很多重复性的,公共的依赖时,可以把这些依赖整合为一个pom,例如把junit, logback, json, spring等等整合为com.example.common项目,它的packing类型为pom,之后引入com.example.common相当于引入很多这些重复性的,公共性的依赖。
3.3 传递性依赖
传递性依赖通常是组合关系,它很容易产生jar包的冲突,所以单独介绍。
在运行项目,或者是编译之前,首先使用maven helper插件分析pom,看是否存在冲突。
当Conflicts下面不存在任何内容时,表示无冲突。
当存在冲突时,可以选择较为常见的,或者是较为稳定的版本,之后排除掉其他版本,鼠标右键,点击exclude即可。有了插件之后非常简单,若要使用命令行去分析,估计是非常困难。我曾经就有过这种痛苦的经历。
3.4 作用域
作用域有两个关键点,第一个是控制作用域的修饰符,或者是作用域的类型。第二个是作用域的适用范围。
例如Java中类,属性的作用域,private, public, default, protected为作用域的类型。
作用域的范围有单个对象,父子对象,同包,公开。
Maven依赖的作用域也是同样的道理。它作用域的范围依据项目生命周期的三个阶段,编译,运行,打包。
作用域的种类,或者是修饰符分为以下6种。
- compile:它是依赖的默认值,它会存在于任何的编译,存在于任何的运行,存在于最终打包的项目中,它的范围是最大的,类似于Java的public。
- test:它会存在于测试下的编译(src/test目录下),测试阶段的运行(junit运行),不会存在于最终打包的项目。它适用于测试的全阶段。
- runtime:它不存在于编译阶段,只存在于运行阶段,存在于最终打包的项目中,在编写代码时,引用这些依赖会报错。
- provided:它表示由相关的运行环境提供依赖,web项目的运行环境通常包含JDK和web server提供的jar。最常见的是servlet.api,它存在于编译,运行阶段,不会存在于最终打包的项目。
- system:它表示由系统提供,通常是指存放于文件系统中的依赖。当无法通过maven引入某个jar包,可以下载jar包,并提供jar包的路径,指定它的作用域为system。
- import:它表示引入的是pom类型的依赖,它通常是公共依赖的集合。
后三种不能算是作用域,应该算是jar包的来源类型。类似于JVM加载class文件时,可以来源于项目,本地磁盘,网络等等。
3.5 其他标签
除与maven坐标有关,依赖有关的标签之外,还有一些其他的标签。下述介绍这些标签。
- classifier:表示JDK的版本,或者是编译的版本,同一个项目可以使用不同版本的JDK去编译,但是需要保证项目编译阶段,运行阶段高于依赖指定的JDK版本。
- systemPath:当scope为system时,指定jar包的物理地址
- optional:optional是指依赖关系的强制性还是按需性,类似于Java单例模式中的懒汉和恶汉。假设A依赖B时,在构建项目A时,是需要强制引入B,当optional为true时,只有引用B依赖时,才会去引入B。实现了B依赖的按需引入。