《Maven项目目录布局以及POM文件》源站链接,阅读体验更佳~
《认识Maven》一文中我们提到过,Maven不仅仅是一个构建工具,同时它还是一个依赖管理工具和项目信息管理工具。在Maven管理依赖的过程中,它就必须有一种机制可以找到我们所依赖的模块,然后帮我们下载依赖,用于工程的构建。如果我们想要使用Maven来管理我们项目的依赖和构建过程,首先需要了解的就是如何让我们的工程成为一个Maven工程,而一个Maven项目的基本目录布局又是怎样的。
Maven项目的标准目录布局
Maven的核心思想只有就是约定优于配置,所以,如果我们不去修改Maven的默认目录布局,一个Maven项目的目录会遵守如下的目录布局约定:
目录 | 说明 |
---|---|
src/main/java | 项目的主代码目录 |
src/main/resources | 项目的主资源目录 |
src/main/filters | 资源过滤器文件,这个目录是非必须的 |
src/main/webapp | Web应用程序主代码目录 |
src/test/java | 测试代码目录 |
src/test/resources | 测试资源目录 |
src/test/filters | 测试资源过滤器文件 |
src/it | 集成测试(主要针对插件) |
src/assembly | 装配描述符 |
src/site | 站点 |
LICENSE.txt | 许可文件 |
NOTICE.txt | 项目所依赖的库所需的通知和属性 |
README.txt | readme文件 |
pom.xml | 项目的描述文件,依赖管理就是在这里进行配置的 |
target/ | 存放所有构建过程中的输出。 |
可以看到,其实上述结构中只有两个子目录,分别是src
和target
,如果在项目中使用了版本管理工具,可能还会存在一个版本管理工具的数据库目录,比如git的.git/目录。
target/
目录用于存放构建的所有输出。 src/
目录包含用于构建项目的所有原始资料、其站点等。 它包含每种类型的子目录:main用于主要构建工件,test用于单元测试代码和资源,站点等等。
对于src/main/
和src/test
这两个目录而言,它们的结构是完全一样的,其中的java子目录用于存放Java语言的源代码,resource目录则用于存放静态资源(该目录下的内容会在编译的时候直接原样复制到编译后的classpath下面)。如果我们在编译的时候还有其他的资源来源,则可以在src/main/
目录下增加新的子目录,比如我们可以把kotlin语言的源代码放置在src/main/kotlin
目录中。
这就是一个Maven项目最标准的目录布局结构,如果由于某些特殊的原因,使得我们的项目不能遵循这个约定的布局,那么我们也可以自己定义适合我们项目的目录布局,这在我们后面的文章中会有所介绍,在我们刚开始学习Maven的时候,还是遵循约定吧。
POM文件
POM(Project Object Model,项目对象模型)是Maven的基本工作单元。它是一个XML文件,包含Maven用于构建项目的项目信息和配置细节。它包含大多数配置项的默认值,例如构建的输出目录默认是target,源代码目录默认是src/main/java等等。当执行任务目标时,Maven会从执行命令的目录中查找POM(一般我们都会把POM文件放置到项目以及各个模块的根目录中),从POM中读取所需的配置信息,然后执行目标。
我们可以在POM中指定的一些配置项包括项目依赖、可执行的插件或目标、构建概要文件等等。还可以指定其他信息,比如项目版本、描述、开发人员、邮件列表等。
pom.xml文件对于Maven来讲就像是Makefile对于make或者是build.xml文件对于ant那样,它是用来描述Maven项目的基本元数据的。我们对Maven项目的依赖管理也是在这个文件中进行配置的。以下就是一个大而全的pom.xml的示例:《大而全的pom文件示例》
不过,在我们实际使用Maven的时候是不可能用到pom文件中所有的配置的。
最小POM
这篇文章中,我们重点要介绍的就是Maven中的坐标和项目的依赖管理。
一个POM中至少要包含如下的配置项:
project
modelVersion
groupId
artifactId
version
如下:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1</version>
</project>
其中POM文件的根节点应该是project,同时modelVersion应该被设置为4.0.0,groupId、artifactId、version则声明了项目的坐标信息。POM需要配置groupId、artifactId和版本号。这三个值形成了项目的完全限定工件名称。它的形式是<groupId>:<artifactId>:<版本>
。对于上面的例子,它的完全限定工件名称是“com.mycompany.app:my-app:1”。
另外,如果没有指定配置细节,Maven将使用它们的默认值。这些默认值之一是打包类型。每个Maven项目都有一个打包类型。如果POM中没有指定,则使用默认值“jar”。
此外,从上面的示例中可以看到在最小的POM中没有指定存储库。如果我们使用最小POM构建项目,它将继承超级POM中的存储库配置。因此,当Maven看到最小POM中的依赖项时,它将知道这些依赖项将从超级POM中指定的https://repo.maven.apache.org/maven2下载。
POM是支持继承的,如果我们没有在POM中显式指定父POM,那么POM会默认继承Super POM,关于POM继承相关的内容,后面会有专门的文章进行介绍。
大家可以看一下Maven3.6.3 Super POM:
通过观察我们其实可以发现,有很多的Maven配置项的默认值其实都是继承自Super POM,其中就有我们上文介绍的Maven项目的默认目录结构。
依赖声明
POM中除了最小POM中提到的元素之外,最常用的应该就是项目的依赖声明了,如下:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1</version>
<!-- 依赖声明 -->
<dependencies>
<dependency>
<groupId>com.mycompany.app</groupId>
<artifactId>my-module</artifactId>
<version>1</version>
</dependency>
</dependencies>
</project>
可以看到依赖声明里面也会包含被依赖的项目的坐标信息,当然这只是最基本的,在依赖声明中有可能还会进行可选性,排除传递等方面的配置,这些我们会在后面依赖调解的相关文章中进行介绍。
Maven坐标
通过上文的介绍我们可以知道,无论是最小POM,还是在POM中对项目进行依赖管理,都需要在POM中使用坐标,因此,了解Maven中的坐标是尤其重要的。
**Maven定义了这样一组规则:世界上任何一个构件都可以使用Maven坐标唯一标识,Maven坐标的元素包括groupId、artifactId、version、packaging、classifier。现在,只要我们提供正确的坐标元素,Maven就能找到对应的构件。**比如说,当需要使用Java5平台上TestNG的5.8版本时,就告诉Maven:“groupId=org.testng;artifactId=testng;version=5.8;classifier=jdk1.5”,Maven就会从仓库中寻找相应的构件供我们使用。
那么,Maven坐标中的每一个元素都分别代表了什么样的信息呢?我们有应该如何对它们进行配置呢?
-
groupId
定义当前Maven项目隶属的实际项目。首先,Maven项目和实际项目不一定是一对一的关系。比如SpringFramework这一实际项目,其对应的Maven项目会有很多,如spring-core、spring-context等。这是由于Maven中模块的概念,因此,一个实际项目往往会被划分成很多模块。其次,groupId不应该对应项目隶属的组织或公司。原因很简单,一个组织下会有很多实际项目,如果groupId只定义到组织级别,artifactId只能定义Maven项目(模块),那么实际项目这个层将难以定义。
groupId的表示方式与Java包名的表示方式类似,通常与域名反向一一对应。
-
artifactId
该元素定义实际项目中的一个Maven项目(模块),推荐的做法是使用实际项目名称作为artifactId的前缀。在默认情况下,Maven生成的构件,其文件名会以artifactId作为开头,如nexus-indexer-2.0.0.jar,使用实际项目名称作为前缀之后,就能方便从一个lib文件夹中找到某个项目的一组构件
-
version
该元素定义Maven项目当前所处的版本,如上例中nexus-indexer的版本是2.0.0。需要注意的是,Maven定义了一套完整的版本规范,以及快照(SNAPSHOT)的概念。对于maven中的版本管理,我们后面会有详细的介绍。
-
packaging
该元素定义Maven项目的打包方式。首先,打包方式通常与所生成构件的文件扩展名对应,如上例中packaging为jar,最终的文件名为nexus-indexer-2.0.0.jar,而使用war打包方式的Maven项目,最终生成的构件会有一个.war文件,不过这不是绝对的。其次,打包方式会影响到构建的生命周期,比如jar打包和war打包会使用不同的命令。最后,当不定义packaging的时候,Maven会使用默认值jar。
-
classifier
该元素用来帮助定义构建输出的一些附属构件。附属构件与主构件对应,如上例中的主构件是nexus-indexer-2.0.0.jar,该项目可能还会通过使用一些插件生成如nexus-indexer-2.0.0-javadoc.jar、nexus-indexer-2.0.0-sources.jar这样一些附属构件,其包含了Java文档和源代码。这时候,javadoc和sources就是这两个附属构件的classifier。这样,附属构件也就拥有了自己唯一的坐标。还有一个关于classifier的典型例子是TestNG,TestNG的主构件是基于Java 1.4平台的,而它又提供了一个classifier为jdk5的附属构件。注意,不能直接定义项目的classifier,因为附属构件不是项目直接默认生成的,而是由附加的插件帮助生成的。
上述5个元素中,groupId、artifactId、version是必须定义的,packaging是可选的(默认为jar),而classifier是不能直接定义的。
我们上文也介绍过,如果我们不做特殊的配置,那么使用Maven进行项目构建所生成的构件的文件名与项目的坐标信息是有关联的,一般的规则为artifactId-version[-classifier].packaging,[-classifier]表示可选。比如上例nexus-indexer的主构件为nexus-indexer-2.0.0.jar,附属构件有nexus-indexer-2.0.0-javadoc.jar。
这里还要强调的一点是,packaging并非一定与构件扩展名对应,比如packaging为maven-plugin的构件扩展名为jar。
此外,Maven仓库的布局也是基于Maven坐标,这一点会在介绍Maven仓库的时候详细解释。
总结
这篇文章中,我们介绍了Maven项目的默认目录布局,以及对于Maven项目来说最基本的配置文件——POM文件,同时我们也介绍了Maven中的坐标的概念。这也是学习Maven首先需要了解的最基本的概念,了解了这些概念之后,我们就可以继续深入对Maven的学习了。
以上就是我对Maven项目的目录布局已经POM文件的基本理解,感谢你耐心读完,本人深知技术水平和表达能力有限,如果文中有什么地方不合理或者你有其他不同的思考和看法,欢迎随时和我进行讨论(laomst@163.com)。