网做网站,wordpress主题框架开发,网站建设 中企动力福州阀门,全球十大搜索引擎Maven学习笔记 Maven的简要介绍Maven的安装和配置Maven的安装Maven安装的常用配置 Maven的使用入门编写pom编写主代码编写测试代码打包和运行使用Archetype生成项目骨架 Maven核心概念的阐述坐标案例分析依赖依赖的范围传递性依赖依赖范围依赖调节可选依赖Maven依赖常用的技巧 … Maven学习笔记 Maven的简要介绍Maven的安装和配置Maven的安装Maven安装的常用配置 Maven的使用入门编写pom编写主代码编写测试代码打包和运行使用Archetype生成项目骨架 Maven核心概念的阐述坐标案例分析依赖依赖的范围传递性依赖依赖范围依赖调节可选依赖Maven依赖常用的技巧 仓库概念布局分类远程仓库的配置快照版本镜像 生命周期与插件生命周期详解插件目标插件绑定插件配置 聚合与继承聚合继承聚合和继承的关系反应堆 使用Nexus建立私服Nexus的仓库与仓库组配置Maven从Nexus下载构件 版本管理何为版本管理Maven的版本号定义约定主干、标签与分支 Maven属性 Maven的简要介绍
Maven主要服务于基于Java平台的项目构建、依赖管理和项目信息管理。
构建是什么
编译、运行单元测试、生成文档、打包和部署等。
Maven的用途及优点
Maven是服务于构建能够帮我们自动化构建过程从清洗、编译、测试到生成报告再到打包和部署。Maven是一个依赖管理工具和项目信息管理工具提供了中央仓库帮助我们自动下载构件。Maven是跨平台的对外提供了一致的操作接口。Maven最大化的消除了构建的重复抽象了构建生命周期。Maven可以标准化构建过程。Maven通过一个坐标系统准确地定位每一个构件artifact也就是通过一组坐标Maven能够找到任何一个Java类库使得我们可以借助它有序的管理依赖。Maven还能帮助我们管理原本分散在项目中各个角落的项目信息包括项目描述、开发者列表、版本控制系统地址、许可证、缺陷管理系统地址等。
Maven的安装和配置
Maven的安装
Step1在安装Maven之前首先要确认是否已经正确安装了JDK。可使用以下命令来检查Java的安装情况
echo %JAVA_HOME% # 检查环境变量是否正确指向了JDK目录
java -version # 运行java命令检测Windows是否可以正常运行Java命令Step2访问Maven的下载页面根据自己的系统选择相应的maven下载文件apache-maven-3.9.5-bin.zip即可。
Step3在Windows本地进行安装将前面下载好的压缩包进行解压到指定目录如果电脑中有多个盘尽量不要安装到C盘。
Step4配置环境变量将Maven安装配置到操作系统环境中。
Step5此电脑鼠标右键点击– 属性 – 高级系统设置 – 环境变量 – 新建系统变量MAVEN_HOME。 Step6编辑path变量–双击Path – 新建变量值%MAVEN_HOME%\bin。
【注】值得注意的是Path环境变量。当我们在cmd中输入命令时Windows首先会在当前目录中寻找可执行文件或脚本如果没有找到Windows会接着遍历环境变量Path中定义的路径。由于将%MAVEN_HOME%\bin添加到了Path中而这里%MAVEN_HOME%实际上是引用了前面定义的另一个变量其值是Maven的安装目录。因此Windows会在执行命令时搜索目录D:Maven\apache-maven-3.8.6\bin而mvn执行脚本的位置就是这里。
Step7打开一个新的cmd窗口这里强调新的窗口是因为新的环境变量配置需要新的cmd窗口才能生效可运行以下命令检查Maven的安装情况
echo %MAVEN_HOME% # 检查环境变量是否正确指向了Maven目录
mvn -v # 运行mvn命令检测Windows是否可以正常运行mvn执行脚本Maven安装的常用配置
设置MAVEN_OPTS环境变量 通常需要设置MAVEN_OPTS的值为-Xms128m-Xmx512m因为Java默认的最大可用内存往往不能够满足Maven运行的需要比如在项目较大时使用Maven生成项目站点需要占用大量的内存如果没有该配置则很容易得到java.lang.OutOfMemeoryError。
Maven的使用入门
编写pom
Maven项目的核心就是pom.xml。POMProject Object Model项目对象模型定义了项目的基本信息用于描述项目如何构建声明项目依赖等。
做一个案例为Hello World项目编写一个简单的pom.xml。
首先新建一个hello-world的文件夹打开该文件夹新建一个pom.xml的文件输入以下内容
?xml version1.0encodingUTF-8?
project xmlnshttps://maven.apache.org/POM/4.0.0xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsdmodelVersion4.0.0/modelVersiongroupIdcom.kang/groupIdartifactIdhello-world/artifactIdversion1.0-SHAPSHOT/versionnameMaven Hello World Project/name
/project代码的第一行是XML头指定了该XML文档的版本和编码方式。紧接着是project元素project是所有pom.xml的根元素它还声明了一些POM相关的命名空间及xsd元素虽然这些元素不是必须的但使用这些属性能够让第三方工具帮助我们快速编辑POM。根元素下第一个元素是modelVersion指定了当前POM模型的版本对于Maven2和Maven3来说只能是4.0.0。这段代码中最重要的是包含groupId、artifactId和version的三行。这三个元素定义了一个项目基本的坐标在Maven中任何的jar、pom和war都是基于这些基本的坐标进行区分的。 groupId定义了项目属于哪个组这个组往往和项目所在的组织或者公司存在关联。例如你的公司是mycom有一个项目为myapp那么groupId就应该是com.mycom.myapp。artifactId定义了当前Maven项目在组中唯一的ID我们为这个Hello World项目定义artifactId为hello-world。在前面的com.mycom.myapp的例子中可能会根据实际情况为不同的子项目模块分配artifactId如myapp-util、myapp-domain、myapp-web等。version指定了Hello World项目当前的版本 —— 1.0-SNAPSHOT。SNAPSHOT意为快照说明该项目还处于开发中是不稳定的版本。随着项目的发展version会不断的更新如升级为1.0、1.1-SNAPSHOT、1.1、2.0等。 最后一个name元素声明了一个对于用户更为友好的项目名称虽然这不是必须的但还推荐为每个POM声明name。
没有任何实际的Java代码我们就能够定义出来一个Maven项目的POM这正是Maven的一大优点。它能够让项目对象模型最大程度地与实际代码相独立我们可以称之为解耦或者正交性。在很大程度上避免了Java代码和POM代码的相互影响。比如项目需要升级版本只需要修改POM而不需要更改Java代码。
编写主代码
项目主代码和测试代码不同主代码会被打包到最终的构建中如jar而测试代码只在运行测试时用到不会被打包。
默认情况下Maven项目的主代码位于src/mian/java目录遵循Maven的约定创建该目录然后在该目录下创建文件com/kang/study/maven/helloworld/HelloWorld.java。具体代码内容如下
package com.kang.study.maven.helloworld;public class HelloWorld{public String sayHello(){return Hello Maven;}public static void main(String[] args){System.out.print(new HelloWorld().sayHello());}
}这是一个简单的Java类它有一个sayHello()方法并将结果返回到控制台上。
【注意】1、绝大多数情况下应该把项目的主代码放到src/main/java目录下而无需其他的配置Maven会自动搜寻该目录找到项目的主代码。2、该Java类的包名com.kang.study.maven.helloworld这与之前的groupId和artifactId相呼应。一般情况下来说项目的Java类的包都应该基于项目的groupId和artifactId。
然后使用Maven进行编译在项目的根目录下运行mvn clean compile会得到以下输出
C:\CodeRoom\hello-worldmvn clean compile
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------ com.kang.study.maven:hello-world ------------------
[INFO] Building Maven Hello World Project 1.0-SHAPSHOT
[INFO] from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- clean:3.2.0:clean (default-clean) hello-world ---
[INFO] Deleting C:\CodeRoom\hello-world\target
[INFO]
[INFO] --- resources:3.3.1:resources (default-resources) hello-world ---
[WARNING] Using platform encoding (GBK actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory C:\CodeRoom\hello-world\src\main\resources
[INFO]
[INFO] --- compiler:3.11.0:compile (default-compile) hello-world ---
[INFO] Changes detected - recompiling the module! :source
[WARNING] File encoding has not been set, using platform encoding GBK, i.e. build is platform dependent!
[INFO] Compiling 1 source file with javac [debug target 1.8] to target\classes
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.700 s
[INFO] Finished at: 2023-11-28T18:22:5508:00
[INFO] ------------------------------------------------------------------------clean告诉Maven清理输出目录target/compile告诉Maven编译项目主代码。从输出中看到Maven首先执行了clean:clean任务删除target/目录。默认情况下Maven构建的所有输出都在target/目录中。接着执行resources:resources任务未定义项目资源暂且略过。最后执行compiler:compiler任务将项目主代码编译至target/classes目录编译好的类为com/kang/study/maven/helloworld/HelloWorld.class。
以上就是Maven在没有额外的配置的情况下就执行了项目的清理和编译任务。
编写测试代码
为了使项目结构保持清晰主代码和测试代码应该分别位于独立的目录中。
Maven项目中默认的主代码目录是src/main/java对应的Maven项目中默认的测试代码目录是src/test/java。因此需要创建此目录再进行测试代码的编写。
?xml version1.0 encodingUTF-8?
project xmlnshttps://maven.apache.org/POM/4.0.0xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsdmodelVersion4.0.0/modelVersiongroupIdcom.kang.study.maven/groupIdartifactIdhello-world/artifactIdversion1.0-SHAPSHOT/versionnameMaven Hello World Project/namedependenciesdependencygroupIdjunit/groupIdartifactIdjunit/artifactIdversion4.12/versionscopetest/scope/dependency/dependencies
/project代码中添加了dependencies元素该元素下可以包含多个dependency元素以声明项目的依赖。这里面添加了一个依赖 —— groupId是junitartifactId是junitversion是4.12。有了这段声明Maven就能够自动下载junit-4.12.jar。上述代码中还有一个值为test的元素scopescope为依赖范围若依赖范围为test则表示该依赖只对测试有效也就是说在测试代码中import JUnit的代码是可以的但是如果在主代码中用import JUnit代码就会编译报错。如果不声明依赖范围那么默认值就是compile表示该依赖对主代码和测试代码都有效。
配置完依赖之后接着就可以写测试类了。在src/test/java目录下创建文件代码如下
package com.kang.study.maven.helloworld;import static org.junit.Assert.assertEquals;import org.junit.Test;public class HelloWorldTest{Testpublic void testSayHello(){HelloWorld helloWorld new HelloWorld();String result helloWorld.sayHello();assertEquals(Hello Maven,result);}
}一个典型的单元测试包含三个步骤
①准备测试类和数据
②执行要测试的行为
③检查结果
上述示例中首先初始化了一个要测试的HelloWorld的实例接着执行sayHello()方法并保存结果到result变量中最后使用JUnit框架的Assert类检查结果是否为我们期望的预期“Hello Maven”。在JUnit中约定所有测试的方法都以应该以Test进行标注。
测试用例编写完毕后就可以调用Maven执行测试了运行命令mvn clean test。
打包和运行
将项目编译、测试之后下一个步骤就是打包package了在没有指定打包类型的情况下使用默认的打包类型是jar运行命令mvn clean package。
我们的HelloWorld项目得到了输出可以复制这个jar文件到其他项目的ClassPath中从而使用HelloWorld类。
如果需要其他的Maven项目直接引用这个jar需要运行命令mvn clean install。
在打包后执行了安装任务install:install。从控制台的输出可以看出该命令将项目输出的jar安装到了Maven本地仓库中只有将Hello World的构建安装到本地仓库之后其他Maven项目才能使用它。
默认打包生成的jar是不能够直接运行的因为带有main方法的类信息不会添加到mainifest中此信息可以在jar中的META-INF/MANIFEST.MF文件中可以看到没有Main-Class一行。为了可以生成可执行的jar文件需要借助maven-shade-plugin配置该插件如下
buildpluginsplugingroupIdorg.apache.maven.plugins/groupIdartifactIdmaven-shade-plugin/artifactIdversion2.4.3/versionexecutionsexecutionphasepackage/phasegoalsgoalshade/goal/goalsconfigurationtransformerstransformer implementationorg.apache.maven.plugins.shade.resource.ManifestResourceTransformermainClasscom.kang.study.maven.helloworld.HelloWorld/mainClass/transformer/transformers/configuration/execution/executions/plugin/plugins
/buildplugin元素在POM中的相对位置应该在projectbuildplugins下面。配置了mainClass为com.kang.study.maven.helloworld.HelloWorld项目在打包时会将该信息放到MANIFEST中。重新执行命令mvn clean install完成之后打开target/目录可以看到两个jar文件hello-world-1.0-SHAPSHOT.jar和original-hello-world-1.0-SHAPSHOT.jar第一个是带有Main-Class信息的可运行的jar第二个是原始的jar。接着打开MANIFEST.MF就可以发现比之前多列一行内容Main-Class: com.kang.study.maven.helloworld.HelloWorld现在就可以在项目的根目录下运行该jar文件了
C:\CodeRoom\hello-worldjava -jar target/hello-world-1.0-SHAPSHOT.jar
Hello Maven至此获得了我们期望的结果Hello Maven。
使用Archetype生成项目骨架
Hello World项目中有一些Maven的约定在项目的根目录中放置pom.xml在src/main/java目录中放置项目的主代码在src/test/java中放置项目的测试代码。
我们称这些基本的目录结构和pom.xml文件内容称为项目的骨架。如果一直重复这些步骤自己创建一个Maven项目会使得工作效率降低。
为此Maven提供了Archetype以帮助我们快速勾勒出项目骨架。还是以Hello World为例我们使用maven archetype来创建该项目的骨架离开当前的Maven项目目录运行命令mvn archetype:generate。
我们会看到一段很长的输出有很多可用的Archetype供选择。每一个Archetype都会有一个编号命令行会给出一个默认的编号对应的是maven-archetype-quickstart回车后按要求输入要创建项目的groupId、artifactId、version以及包名package。然后Archetype会根据我们提供的信息创建项目骨架。
......
Define value for property groupId: com.kang.study.maven
Define value for property artifactId: hello-world-auto-generate
Define value for property version 1.0-SNAPSHOT: :
Define value for property package com.kang.study.maven: : com.kang.study.maven.helloworldauto
Confirm properties configuration:
groupId: com.kang.study.maven
artifactId: hello-world-auto-generate
version: 1.0-SNAPSHOT
package: com.kang.study.maven.helloworldautoY: : Y
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Archetype: maven-archetype-quickstart:1.4
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: com.kang.study.maven
[INFO] Parameter: artifactId, Value: hello-world-auto-generate
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: package, Value: com.kang.study.maven.helloworldauto
[INFO] Parameter: packageInPathFormat, Value: com/kang/study/maven/helloworldauto
[INFO] Parameter: package, Value: com.kang.study.maven.helloworldauto
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: groupId, Value: com.kang.study.maven
[INFO] Parameter: artifactId, Value: hello-world-auto-generate
[INFO] Project created from Archetype in dir: C:\CodeRoom\hello-world-auto-generate
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 49.432 s
[INFO] Finished at: 2023-11-29T13:05:3708:00
[INFO] ------------------------------------------------------------------------在当前目录下Archetype插件会创建一个名为hello-world-auto-generate我们定义的artifactId的子目录从中可以看到项目的基本结构
基本的pom.xml已经被创建里面包含了必要的信息以及一个junit依赖主代码目录src/main/java已经被创建在该目录下还有一个Java类com.kang.study,maven.helloworldauto.App注意这里使用到了刚才定义的包名而这个类也仅仅只有一个简单的输出Hello World的main方法测试代码目录src/test/java也被创建好了并且包含了一个测试用例com.kang.study.maven.helloworldauto.AppTest。
Archetype可以帮助我们迅速地构建起项目的骨架在前面的例子中我们完全可以在Archetype生成的骨架的基础上开发Hello World项目以节省大量时间。
Maven核心概念的阐述
坐标
Maven坐标为各种构件引入了秩序任何一个构件都必须明确定义自己的坐标而一组Maven坐标是通过一些元素来定义的有groupId、artifactId、version、packaging、classifier。下面详细介绍一下各个坐标元素
groupIdorg.sonatype.nexus/groupId
artifactIdnexus-indexer/artifactId
version2.0.0/version
packagingjar/packaginggroupId定义当前项目隶属的实际项目 首先Maven项目和实际项目不一定是一对一的关系。这是由于Maven中模块的概念因此一个实际项目往往会被划分成很多模块其次groupId不应该对应项目隶属的组织或公司。因为一个组织下会有很多实际项目如果groupId只定义到了组织级别的话artifactId只能定义Maven项目模块那么实际项目这一层将很难被定义groupId的表示方式与Java包名的方式类似通常将域名反向一一对应。例如groupId为org.sonatype.nexusorg.sonatype表示Sonatype公司建立的一个非盈利性组织nexus表示Nexus这一实际项目该groupId与域名nexus.sonatype.org对应。 artifactId定义实际项目中的一个Maven项目模块 推荐的做法是使用实际项目名称作为artifactId的前缀。比如上面例子中的artifactId是nexus-indexer使用了实际项目名nexus作为前缀这样方便寻找实际构件默认情况下Maven生成的构件其文件名会以artifactId作为开头如nexus-indexer-2.0.0.jar使用实际项目名称作为前缀之后就能方便从一个lib文件夹中找到某个项目的一组构件。 version定义Maven项目当前所处的版本packaging定义Maven项目的打包方式 首先打包方式通常与所生成构件的文件扩展名对应如packaging为jar最终的文件名为nexus-indexer-2.0.0.jar如果使用war打包方式的话最终的文件为以.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源代码和文档。不能直接定义项目的classifier因为附属构件不是项目直接默认生成的而是由附加的插件帮助生成的。
以上的5个元素中groupId、artifactId、version是必须声明的packaging是可选的默认为jarclassifier是不能直接定义的。
项目构件的文件名与坐标是相对应的一般规则为artifactId-version[-classfier].packaging[-classfier]是可选的。
案例分析
我们用一个实际的案例来巩固我们前面学到的一些内容我们现在需要写整个案例中的其中一个模块email负责发送账户激活的电子邮件。
首先先构建这个模块account-email的POM
project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersiongroupIdcom.kang.study.account/groupIdartifactIdaccount-email/artifactIdversion1.0.0-SNAPSHOT/versionpackagingjar/packagingnameaccount-email/nameurlhttp://maven.apache.org/urlpropertiesproject.build.sourceEncodingUTF-8/project.build.sourceEncoding/propertiesdependenciesdependencygroupIdorg.springframework/groupIdartifactIdspring-core/artifactIdversion2.5.6/version/dependencydependencygroupIdorg.springframework/groupIdartifactIdspring-beans/artifactIdversion2.5.6/version/dependencydependencygroupIdorg.springframework/groupIdartifactIdspring-context/artifactIdversion2.5.6/version/dependencydependencygroupIdorg.springframework/groupIdartifactIdspring-context-support/artifactIdversion2.5.6/version/dependencydependencygroupIdjavax.mail/groupIdartifactIdmail/artifactIdversion1.4.1/version/dependencydependencygroupIdcom.icegreen/groupIdartifactIdgreenmail/artifactIdversion1.3.1b/versionscopetest/scope/dependencydependencygroupIdjunit/groupIdartifactIdjunit/artifactIdversion3.8.1/versionscopetest/scope/dependency/dependenciesbuildpluginManagementpluginsplugingroupIdorg.apache.maven.plugins/groupIdartifactIdmaven-compiler-plugin/artifactIdconfigurationsource1.8/sourcetarget1.8/target/configuration/plugin/plugins/pluginManagement/build
/project由分析可知
groupIdcom.kang.study.account由于该模块属于账户注册服务项目的一部分因此其groupId对应了account项目artifactIdaccount-emailartifactId以account作为前缀以方便区别其他的模块的构建version1.0.0-SNAPSHOT1.0.0-SNAPSHOT表示该版本还处于开发中还不稳定dependencies元素其包含了多个dependency子元素这是POM定义项目依赖的位置其中有三个元素groupId、artifactId、version这便是依赖的坐标任何一个Maven项目都需要定义自己的坐标最后POM中有一段关于maven-compiler-plugin的配置其目的是开启Java8的支持。
依赖
一个依赖声明可以包含如下的一些元素
project......dependenciesdependencygroupId.../groupIdartifactId.../artifactIdversion.../versiontype.../typescope.../scopeoptional.../optionalexclusionsexclusion....../exclusion/exclusions/dependency/dependencies
/project根元素project下的dependencies可以包含一个或者多个dependency元素以声明一个或多个项目依赖。每个依赖可以包含的元素有
groupId、artifactId、version依赖的基本坐标对于任何一个依赖来说基本坐标是最重要的Maven根据坐标才能找到需要的依赖type依赖的类型对应于项目坐标定义的packaging。大部分情况下该元素不予声明其默认值为jarscope依赖的范围optional标记依赖是否可选exclusions用来排除传递性依赖。
依赖的范围
依赖范围就是用来控制依赖与这三种classpath编译classpath、测试classpath、运行classpath的关系。
compile编译依赖范围 如若没有指定默认使用该依赖范围。使用此依赖范围的Maven依赖对于编译、测试、运行这三种classpath都有效。 test测试依赖范围 使用此依赖范围的Maven依赖只对测试classpath有效在编译主代码或运行项目的使用时无法使用此类依赖。 provided已提供依赖范围 使用此依赖范围的依赖对于编译和测试classpath都有效但运行时无效。 runtime运行时依赖范围 使用此依赖范围的依赖对于测试和运行classpath都有效但在编译主代码时无效。 system系统依赖范围 使用此依赖范围的依赖对于编译和测试classpath都有效但运行时无效。使用system范围的依赖时必须通过systemPath元素显示地指定依赖文件的路径。由于此类依赖设备不是通过Maven仓库解析而是往往与本机系统绑定可能造成构建的不可移植因此应该谨慎使用。 import导入依赖范围。该依赖范围不会对三种classpath产生实际的影响。
传递性依赖
在Maven中传递性依赖是指当一个项目依赖于其他项目时它可以自动获取到被依赖项目所需要的依赖项。换句话说当一个项目依赖于另一个项目那么这个项目所依赖的库或者jar文件也会被自动加入到该项目的依赖项中。
举个例子假设项目A依赖于项目B而项目B依赖于项目C。在项目A的pom.xml中只需要声明对项目B的依赖并没有显式声明对项目C的依赖。当使用Maven构建项目A时Maven会自动检测到项目B的依赖关系并将项目C的依赖项添加到项目A的构建路径中。
依赖范围
依赖范围不仅可以控制依赖与三种classpath的关系还对传递性依赖产生影响。假设A依赖于B、B依赖于CA是B的第一直接依赖B是C的第二直接依赖C是A的传递性依赖。
第一直接依赖的范围和第二直接依赖的范围决定了传递性依赖的范围最左边一列表示第一直接依赖范围最上面一行表示第二直接依赖的范围中间的单元格表示传递性依赖范围。
暂时无法在飞书文档外展示此内容
通过上表我们可以看出这样的规律
当第二直接依赖的范围是compile的时候传递性依赖的范围与第一直接依赖的范围一致当第二直接依赖的范围是test的时候依赖不会得以传递当第二直接依赖的范围是provided的时候只传递第一直接依赖范围也为provided的依赖且传递性依赖的范围同样为provided当第二直接依赖的范围是runtime的时候传递性依赖的范围与第一直接依赖的范围一致但compile例外此时传递性依赖的范围为runtime。
依赖调节
依赖调节是指在解决依赖冲突时选择最合适的依赖项的过程。当一个项目具有多个依赖项有可能会出现同一依赖项的不同版本或者不同依赖项之间的冲突这就需要通过依赖调解来解决这个问题。
依赖调节原则
第一原则路径最近者优先。第二原则第一声明者优先。在路径长度相同的情况下声明依赖最靠前的依赖被解析使用
可选依赖
可选依赖是在 Maven 中用于标记某个依赖项为可选的一种机制。可选依赖项通常是指在特定的环境下或者满足特定条件时才需要引入的依赖。
当一个依赖项被标记为可选时Maven在构建项目时不会强制要求引入该依赖项即使该依赖项在项目中没有直接引用也不会报错。但是如果该依赖项在项目中被明确引用了Maven会尝试解析和加载该可选依赖项。
通过在pom.xml中的元素中添加true标记可以将某个依赖项标记为可选依赖。示例代码如下
dependencygroupIdcom.example/groupIdartifactIdexample-library/artifactIdversion1.0.0/versionoptionaltrue/optional
/dependency【注意】在理想情况下是不应该使用可选依赖的使用可选的原因可能是某个项目实现了多个特性在面向对象设计中有个单一职责性原则意指一个类应该只有一项职责而不是糅合太多的功能。
Maven依赖常用的技巧 排除依赖 在 Maven 中有时候我们希望排除某些传递性依赖项即不将它们包含在项目构建中。这时可以使用 Maven 的排除依赖机制。 排除依赖的作用是在 pom.xml 文件中的相关依赖项中指定不需要的传递性依赖使其不会被引入到项目中。通常在某个依赖项的 dependency 元素中使用 exclusions 子元素指定要排除的依赖项。 dependencygroupIdcom.example/groupIdartifactIdexample-library/artifactIdversion1.0.0/versionexclusionsexclusiongroupIdorg.unwanted/groupIdartifactIdunwanted-dependency/artifactId/exclusion/exclusions
/dependency上述示例中example-library 依赖的传递性依赖项中的 unwanted-dependency 将被排除不会被包含在项目构建中。 需要注意一点的是在排除依赖项时需要明确指定其 groupId 和 artifactId以确保准确排除目标依赖项。排除依赖也可以一次性排除多个依赖项只需在 exclusions 下添加多个 exclusion 元素即可。 排除依赖的机制在一些情况下非常有用比如解决依赖冲突或避免引入不需要的依赖项。不过使用排除依赖时要注意慎重确保准确理解依赖关系并明确排除的依赖项对项目的影响。 归类依赖 在 Maven 中可以通过使用 dependencyManagement 元素以及 dependency 元素的组合来对依赖进行分类和管理。 dependencyManagement在 Maven 项目的顶层 POM 文件中通过 dependencyManagement 元素可以集中管理项目中所有模块的依赖版本。在该元素内可以列出全部依赖项并指定它们的版本号。这样项目中的各个模块只需要声明依赖项的 groupId 和 artifactId而不用再指定版本号Maven 会根据 dependencyManagement 中的配置自动管理依赖版本。 dependencyManagementdependenciesdependencygroupIdcom.example/groupIdartifactIdexample-library/artifactIdversion1.0.0/version/dependency!-- 其他依赖项 --/dependencies
/dependencyManagementdependency在每个模块的 POM 文件中通过 dependency 元素引入实际需要的依赖项。在 dependency 元素中指定依赖项的 groupId、artifactId以及任何其他必要的信息不需要引入version。 示例 dependenciesdependencygroupIdcom.example/groupIdartifactIdexample-library/artifactId/dependency!-- 其他依赖项 --
/dependencies通过这样的方式依赖项可以被有效地归类和管理。dependencyManagement 提供了一个统一的地方定义依赖版本避免重复代码确保依赖版本的一致性。而每个模块的 POM 文件中只需声明实际需要的依赖项简化了项目的配置和维护工作。 需要注意的是dependencyManagement 中的依赖项并不会真正引入到项目中它们只是为了统一管理依赖版本。只有在模块的 POM 文件中通过 dependency 元素引入实际依赖项才会将其包含在项目构建中。 在Maven中可以使用properties元素定义Maven属性有了这个属性定义之后Maven运行的时候会将POM中所有${example-library-version}都替换为实际值1.0.0。也就是说使用美元符号和大括弧的方式引用Maven属性。 propertiesexample-library-version1.0.0/example-library-version
/properties
dependenciesdependencygroupIdcom.example/groupIdartifactIdexample-library/artifactIdversion${example-library-version}/version/dependency
/dependencies仓库
概念
在Maven世界中任何一个依赖、插件或者项目构建的输出都可以称为构件。任何一个构件都有一组唯一的标识。得益于坐标机制任何Maven项目使用任何一个构件的方式是完全相同的在此基础上Maven可以在某一个位置统一存储所有Maven项目共享的构件这个统一的位置就是仓库。
实际的Maven项目将不再各自存储其依赖文件它们只需要声明这些依赖的坐标在需要的时候Maven会自动根据坐标找到仓库中的构件并使用它们。
布局
任何一个构件都有其唯一的坐标根据这个坐标可以定义其在仓库中的唯一存储路径这便是Maven的仓库布局方式。示例如下
log4j:log4j:1.2.15这一依赖其对应的仓库路径为log4j/log4j/1.2.15/log4j-1.2.15.jar该路径与坐标的大致对应关系为groupId/artifactId/version/artifactId-version.packaging
分类
对于Maven来说仓库只分为两种本地仓库和远程仓库。
当Maven根据坐标寻找构件的时候首先会查看本地仓库如果本地仓库存在此构件则直接使用如果本地仓库不存在此构件或者需要查看是否有更新的的构件版本Maven就会去远程仓库去查找找到需要的构件之后下载到本地仓库再进行使用如果本地仓库和远程仓库都没有需要的构件Maven则会报错。
特殊的远程仓库
中央仓库 中央仓库是Maven核心自带的远程仓库它包含了绝大部分开源的构件。在默认配置下当本地仓库没有Maven需要的构件的时候他就会尝试从中央仓库下载。 私服 为了节省带宽和时间在局域网内架设一个私有的仓库服务器用其代理所有外部的远程仓库内部的项目还能部署到私服上供其他项目使用。 其他公共库 除了中央仓库和私服还有很多其他公开的远程仓库。
本地仓库
本地仓库是 Maven 在本地计算机上存储构建所需依赖项的地方。当你执行 Maven 构建时Maven 会自动下载所需的依赖项并缓存到本地仓库中。默认情况下本地仓库位于用户的主目录下的 .m2 文件夹中。我们也可以自定义本地仓库目录地址将Maven安装目录下的settings.xml文件进行修改localRepository元素的值为想要的仓库地址。
localRepositoryD:\java\repository\/localRepository远程仓库
安装好Maven后如果不执行任何Maven命令本地仓库目录是不存在的。当用户输入第一条Maven命令后Maven才会创建本地仓库然后根据配置和需要从远程仓库下载构件到本地仓库。
Maven 支持从远程仓库下载依赖项。远程仓库是指存储 Maven 依赖项的中央服务器例如 Maven 中央仓库。当 Maven 在本地仓库中找不到所需的依赖项时它会自动从远程仓库下载。Maven 可以配置多个远程仓库在 settings.xml 配置文件中指定远程仓库的 URL以便引入第三方库和插件。
中央仓库
Maven 中央仓库是 Maven 社区提供的一个公共远程仓库存储了大量的开源库和常见的Maven构建依赖项。中央仓库是 Maven 默认使用的远程仓库当本地仓库没有所需的依赖项时Maven 会自动从中央仓库进行下载。中央仓库是Maven构建中最常用的远程仓库。
私服
私服是指由组织或个人自行搭建和维护的 Maven 仓库。私服可以用于存储公司内部开发的库、插件和其他自定义构建资源。它可以作为组织内部的 “中央仓库”使团队成员可以共享和管理内部构建依赖项。私服可以通过配置 Maven 的 settings.xml 文件来使用。
私服是一种特殊的的远程仓库它是架设在局域网内的仓库服务私服代理广域网上的远程仓库供局域网内的Maven用户使用。当Maven需要下载构件的时候它从私服请求如果私服上不存在此构件则从外部的远程仓库下载缓存在私服上之后再为Maven的下载请求提供服务。此外一些无法从外部仓库下载到本地的构件也能从本地上传到私服上供大家使用。
建立私服的好处
节省自己的外网带宽 大量的对于外部仓库的重复请求会消耗很大的带宽利用私服代理外部仓库之后对外的重复构件下载得以消除即降低外网带宽的压力。 加速Maven构建 不断的连接请求内部仓库是十分耗时的但是Maven的一些内部机制要求Maven在执行构件的时候不停地检查远程仓库数据。当项目配置了很多外部远程仓库的时候构建的速度会被大大降低。使用私服可以很好地解决这一问题当Maven只需要检查局域网内私服的数据时构建的速度便得以很大程度的提高。 部署第三方构件 组织内部生成的私有构件肯定无法从外部仓库获得建立私服之后便可以将构件部署到这个内部的仓库中供内部的Maven项目进行使用。 提高稳定性增强控制 当Internet不稳定的时候Maven构建也会变得不稳定甚至无法构建。使用私服后即使暂时没有Internet连接由于私服中已经缓存了大量构件Maven也能正常运行。 降低中央仓库的负荷
远程仓库的配置
默认的中央仓库无法满足项目的需求可能项目需要的构件存在于另外一个远程仓库中。下面以JBoss Maven仓库为例在POM中配置使用JBoss Maven仓库
project......repositoriesrepositoryidjboss/idnameJBoss Repository/nameurlhttp://repository.jboss.com/maven2//urlreleasesenabledtrue/enabled/releasessnapshotsenabledfalse/enabled/snapshotslayoutdefault/layout/repository/repositories......
/project在repositories元素下可以使用repository子元素声明一个或多个远程仓库。
该例中声明了一个id为jboss名称为JBoss Repository的仓库。任何一个仓库声明的id必须是唯一的Maven自带的中央仓库使用的id为central如果其他的仓库声明也使用该id就会覆盖中央仓库的配置。该配置的url指向了仓库的地址一般来说该地址都基于http协议Maven用户可以在浏览器中打开仓库地址浏览构件。releases用来控制Maven对于发布版构件的下载其中enabled值为true表示开启JBoss仓库的发布版本下载支持。snapshots用来控制Maven对于快照版构件的下载其中enabled值为false表示关闭JBoss仓库的快照版本的下载支持。layout元素值default表示仓库的布局是Maven2或者Maven3的默认布局而不是Maven1的默认布局。releases和snapshots除了enabled还包含两个子元素updatePolicy和checksumPolicy
snapshotsenabledtrue/enabledupdatePolicydaily/updatePolicychecksumPolicyignore/checksumPolicy
/snapshotsupdatePolicy用来配置Maven从远程仓库检查更新的频率默认的值是daily表示Maven每天检查一次。其他取值有never - 从不检查更新always - 每次构建都检查更新interval:X - 每隔X分钟检查一次更新X为任意整数。checksumPolicy用来配置Maven检查检验和文件的策略。当构件被部署到Maven中央仓库中时会同时部署对应的校验和文件。当checksumPolicy的值为默认的warn时Maven会在执行构建时输出警告信息其他可用的值包括fail - Maven遇到校验和错误就让构建失败ignore - 使Maven完全忽略校验和错误。
远程仓库的认证
大部分远程仓库无须认证就可以访问但有时候处于安全方面考虑需要提供认证信息才能访问一些远程仓库。配置认证信息和配置仓库信息不同仓库信息可以直接配置在POM文件中但是认证信息必须配置到settings.xml文件中。
settings......serversserveridmy-proj/idusernamerepo-user/usernamepasswordrepo-pwd/password/server/servers......
/settingsMaven使用settings.xml文件中的servers元素及其server子元素配置仓库认证信息。
上述案例中该仓库的认证用户名为repo-user认证密码为repo-pwd。这里的元素id必须于POM中需要认证的repository元素的id完全一致
部署至远程仓库
Maven除了可以对项目进行编译、测试、打包以外还能将项目生成的构建部署到仓库中。需要编辑pom.xml文件并且配置distributionManagement元素。
project......distributionManagementrepositoryidproj-releases/idnameProj Release Repository/nameurlhttp://192.168.1.100/content/repositories/proj-releases/url/repositorysnapshotRepositoryidproj-snapshots/idnameProj Snapshot Repository/nameurlhttp://192.168.1.100/content/repositories/proj-snapshots/url/snapshotRepository/distributionManagement......
/projectdistributionManagement包含repository和snapshotRepository子元素前者表示发布版本构件的仓库后者表示快照版本的仓库。这两个元素下都需要配置id、name、urlid表示仓库的唯一标识name是为了方便人阅读url表示该仓库的地址。往远程仓库部署构件的时候往往需要认证。配置认证信息前面的介绍中已经解释过了简言之就是在settings.xml中创建一个server元素需要注意的就是其id与仓库的id要相匹配并配置正确的认证信息。配置正确后执行mvn clean deployMaven就会将项目输出的构件部署到配置对应的远程仓库中。
快照版本
在 Maven 中快照版本是指开发中的项目版本这些版本仍处于不稳定的状态可能会发生变化。快照版本通常用于开发过程中的测试、实验或持续集成构建。
快照版本的命名约定如下在项目的版本号后添加 “-SNAPSHOT” 后缀。例如一个项目的稳定版本可能是 1.0而该项目的快照版本可能是 1.0-SNAPSHOT。
在一些情况下当使用快照版本时可能会发现一些依赖项无法稳定地被下载或使用因为快照版本是不稳定的可能会在每次构建中有所变化。快照版本只应该在组织内部的项目或模块间依赖使用此时组织对这些快照版本的依赖具有完全的理解及控制权。项目不应该依赖于任何组织外部的快照版本依赖由于快照版本的不稳定性这样的依赖会造成潜在的危险。因此在生产环境中建议使用稳定的正式版本而不是快照版本。
镜像
如果仓库X可以提供仓库Y存储的所有内容那么就可以认为X是Y的一个镜像。任何一个可以从仓库Y获得的构件都能够从它的镜像中获取。示例http://maven.net.cn/content/groups/public/是中央仓库http://repo1.maven.org/maven2/在中国的镜像由于地理位子的因素该镜像往往能够提供比中央仓库更快的服务。编辑settings.xml来配置Maven使用镜像来替换中央仓库。
settings......mirrorsmirroridmaven.net.cn/idnameone of the central mirrors in China/nameurlhttp://maven.net.cn/content/groups/public//urlmirrorOfcentral/mirrorOf/mirror/mirrors......
/settings示例中mirrorOf的值为central表示该配置为中央仓库的镜像任何对于中央仓库的请求都会转至该镜像其他镜像配置方式同理。
关于镜像的一个更为常见的用法是结合私服由于私服可以代理任何外部的公共仓库因此对于组织内部的Maven用户来说使用一个私服地址就等于使用了所有需要的外部仓库这可以将配置集中到私服从而简化Maven本身的配置。在这种情况下任何需要的构件都可以从私服获得私服就是所有仓库的镜像。
配置使用私服作为镜像示例
settings......mirrorsmirroridinternal-repository/idnameInternal Repository Manager/nameurlhttp://192.168.1.100/maven2//urlmirrorOf*/mirrorOf/mirror/mirrors......
/settingsmirrorOf*/mirrorOf匹配所有远程仓库mirrorOfexternal:*/mirrorOf匹配所有远程仓库使用localhost的除外使用file://协议的除外。换言之匹配所有不在本机上的远程仓库。mirrorOfrepo1repo2/mirrorOf匹配仓库repo1和repo2使用逗号分隔多个远程仓库。mirrorOf*repo1/mirrorOf匹配所有远程仓库repo1除外使用感叹号将仓库从匹配中排除。
【注意】由于镜像仓库完全屏蔽了被镜像仓库当镜像仓库不稳定或者停止服务的时候Maven仍将无法访问被镜像仓库因而将无法下载构件。
生命周期与插件
生命周期详解
Maven拥有三套相互独立的生命周期它们分别是clean、default和site。clean生命周期的目的是清理项目、default生命周期目的是构建项目site生命周期的目的是建立项目站点。
每个生命周期包含一些阶段这些阶段是有顺序的后面的阶段依赖于前面的阶段去执行。三套生命周期是相互独立的互相之间并不受影响。
clean生命周期
此生命周期的目的是清理项目
pre-clean - 执行一些清理前需要完成的工作clean - 清理上一次构建生成的文件post-clean - 执行一些清理后需要完成的工作
default生命周期
此生命周期定义了真正构建时所需要执行的所有步骤它是所有生命周期中最核心的部分下面对各阶段进行解释
暂时无法在飞书文档外展示此内容
site生命周期
此生命周期的目的是建立和发布项目站点Maven能够基于POM所包含的信息自动生成一个友好的站点方便团队交流和发布项目信息
pre-site - 执行一些在生成项目站点之前需要完成的工作site - 生成项目站点文档post-site - 执行一些在生成项目站点之后需要完成的工作site-deploy - 将生成的项目站点发布到服务器上
命令行与生命周期
mvn clean - 该命令调用clean生命周期中的clean阶段。实际执行的阶段为clean的生命周期的pre-clean和clean阶段mvn test - 该命令调用default生命周期的test阶段。实际的执行阶段为default生命周期的validate、initialize等直到test的所有阶段。mvn clean install - 该命令调用clean生命周期的clean阶段和default生命周期的install阶段。实际执行的阶段为clean生命周期的pre-clean、clean阶段以及default生命周期的从validate到install的所有阶段。mvn clean deploy site-deploy - 该命令调用clean生命周期的clean阶段、default生命周期的deploy阶段以及site生命周期的site-deploy阶段。实际执行的阶段为clean生命周期的pre-clean、clean阶段default生命周期的所有阶段以及site生命周期的所有阶段。
插件目标
在 Maven 中Maven的核心仅仅定义了抽象的生命周期具体的任务是交由插件完成的插件以独立的构件形式存在。插件目标是针对插件的具体操作或任务。每个 Maven 插件可以定义不同的目标用于完成不同的功能和任务。
插件绑定
Maven的生命周期的阶段和插件的目标是相互绑定的用以完成某个具体的构建任务。
内置绑定
clean生命周期仅有pre-clean、clean和post-clean三个阶段其中的clean与maven-clean-plugin:clean绑定。maven-clean-plugin仅有clean这一目标其作用就是删除项目的输出目录。
site生命周期有pre-site、site、post-site和site-deploy四个阶段其中site和maven-site-plugin:site相互绑定site-deploy和maven-site-plugin:deploy相互绑定其中site目标用来生成项目站点deploy目标用来将项目站点部署到远程服务器上。
default生命周期的阶段与插件目标的绑定关系由项目打包类型所决定。最常见、最重要的打包类型是jar它也是默认的打包类型。基于这种打包类型的项目其default生命周期的内置插件绑定关系及具体任务如下所示
暂时无法在飞书文档外展示此内容
自定义绑定
Maven 插件的自定义绑定允许将某个插件的目标绑定到生命周期的某个阶段上。
buildpluginsplugingroupIdorg.apache.maven.plugins/groupIdartifactIdmaven-source-plugin/artifactIdversion2.1.1/versionexecutionsexecutionidattach-sources/idphaseverify/phasegoalsgoaljar-no-fork/goal/goals/execution/executions/plugin/plugins
/build在POM的build元素下的plugins子元素中声明插件的使用该例中用到的是maven-source-plugin其groupId为org.apache.maven.plugins这是Maven官方插件的groupId紧接着artifactId为maven-source-pluginversion为2.1.1。除了基本的插件坐标以外还有插件执行配置executions下每个execution子元素可以用来配置执行一个任务。该例中配置了一个id为attach-sources的任务通过phrase配置将其绑定到verify生命周期阶段上再通过goals配置指定要执行的插件目标。
插件配置
命令行插件配置
在日常使用中我们会使用命令行输入并执行Maven命令。很多插件目标的参数都支持从命令行配置可以在Maven命令中使用-D参数并伴随一个参数键参数值的形式来配置插件目标的参数。例如
mvn install -Dmaven.test.skiptrue
此条命令可以在构建 Maven 项目时跳过运行测试。
POM中插件全局配置
有的参数的值从项目创建到项目发布基本不会改变在这种情况下在POM文件中一次性配置就显然比重复命令行输入更方便。例如我们通常需要配置maven-compile-plugin告诉它编译Java1.8版本的源文件生成与JVM1.8兼容的字节码文件。
buildpluginsplugingroupIdorg.apache.maven.plugins/groupIdartifactIdmaven-compiler-plugin/artifactIdversion2.1/versionconfigurationsource1.8/sourcetarget1.8/target/configuration/plugin/plugins
/buildPOM中插件任务配置
除了可以为插件配置全局参数外用户还可以为某个插件任务配置特定的参数。
以maven-antrun-plugin为例它有一个目标run可以用来在Maven中调用Ant任务。用户将maven-antrun-plugin:run绑定到多个生命周期阶段上再加以不同的配置就可以让Maven在不同的生命阶段执行不同的任务。
buildpluginsplugingroupIdorg.apache.maven.plugins/groupIdartifactIdmaven-antrun-plugin/artifactIdversion1.3/versionexecutionsexecutionidant-validate/idphasevalidate/phasegoalsgoalrun/goal/goalsconfigurationtasksechoIm bound to validate phase./echo/tasks/configuration/executionexecutionidant-verify/idphaseverify/phasegoalsgoalrun/goal/goalsconfigurationtasksechoIm bound to verify phase./echo/tasks/configuration/execution/executions/plugin/plugins
/build首先maven-antrun-plugin:run与validate阶段绑定从而构成一个id为ant-validate的任务。插件全局配置中的configuration元素位于plugin元素下面而这里的configuration元素则位于execution元素下表示这是特定任务的配置而非插件整体的配置。这个ant-validate任务配置了一个echo Ant任务向命令行输出一段文字表示该任务是绑定到validate阶段的。第二个任务的id为ant-verify它绑定到了verify阶段同样它也输出一段文字到命令行告诉该任务绑定到了verify阶段。
聚合与继承
聚合
Maven 的聚合指的是将多个 Maven 项目合并为一个项目以便进行一起构建、测试、部署和管理。
在 Maven 中可以使用多个 module 元素来指定需要聚合的项目。父 pom.xml 中指定项目的 module 如下
modulesmodulemodule1/modulemodulemodule2/modulemodulemodule3/module!-- ... --
/modules将多个项目聚合在一起后 Maven 可以在根目录下执行一系列的 Maven 命令对所有子模块一同进行处理。例如执行 mvn clean package 命令可以对所有的模块进行清理和打包操作。
具体举例来说现有两个Maven模块account-email和account-persist用来实现用户注册的功能。如果我们想要一次性构建两个模块我们就需要重新创建一个额外的名为account-aggregator的模块通过这个模块来构建整个项目的所有模块。account-aggregator作为一个Maven项目并且是一个聚合项目其POM又有特殊的地方。
?xml version1.0 encodingUTF-8?
project xmlnshttp://maven.apache.org/POM/4.0.0xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersiongroupIdcom.kang.study.account/groupIdartifactIdaccount-aggregator/artifactIdversion1.0.0-SNAPSHOT/versionpackagingpom/packagingnameAccount Aggregator/namemodulesmoduleaccount-email/modulemoduleaccount-persist/module/modulespropertiesmaven.compiler.source8/maven.compiler.sourcemaven.compiler.target8/maven.compiler.targetproject.build.sourceEncodingUTF-8/project.build.sourceEncoding/properties/project上述的POM依旧使用与其他两个模块一样的groupIdartifactId为独立的account-aggregate版本与其他两个模块也一样。一个特殊的地方是指定了packaging为pom。对于聚合模块来说其打包方式packaging的值必须为pom否则无法构建。第二个特殊的地方是元素modules这是实现聚合最核心的配置。可以通过在一个打包方式为pom的Maven项目中声明任意数量的module元素来实现模块的聚合。一般情况下为了方便构建项目通常将聚合模块放在项目目录的最顶层其他模块作为聚合模块的子目录存在。聚合模块仅仅是帮助聚合其他模块构建的工具它本身并无实质的内容。
继承
Maven 继承指的是子模块可以继承父模块的依赖和插件配置等信息从而减少模块之间的重复配置提高项目的可维护性和一致性。
在 Maven 中继承通常发生在一个父项目和若干个子项目之间。父项目中包含了一些通用的配置信息如依赖、插件、属性等子项目可以继承这些信息同时自定义和重写部分配置以满足自身的需求。在 Maven 中会通过 parent 元素来声明项目之间的继承关系。
举例说明我们在account-aggregate下创建一个名为account-parent的子目录然后在该子目录建立一个除account-aggregate之外模块的父模块并创建一个pom.xml
?xml version1.0 encodingUTF-8?
project xmlnshttp://maven.apache.org/POM/4.0.0xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersiongroupIdcom.kang.study.maven/groupIdartifactIdaccount-parent/artifactIdversion1.0.0-SNAPSHOT/versionpackagingpom/packagingnameAccount Parent/namepropertiesmaven.compiler.source8/maven.compiler.sourcemaven.compiler.target8/maven.compiler.targetproject.build.sourceEncodingUTF-8/project.build.sourceEncoding/properties/project此父模块使用了与其他模块一样的groupId、version使用artifactId为account-parent表示这是一个父模块其打包方式packaging仍为pom作为父模块的POM打包类型必须为pom。父模块是为了消除配置的重复因此它本身不包含除POM以外的其他项目文件。
有了父模块就需要其他模块来继承它。
子项目继承父项目的配置可以通过在 pom.xml 文件中包含 parent 元素来实现
project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersionparentgroupIdcom.kang.study.account/groupIdartifactIdaccount-parent/artifactIdversion1.0.0-SNAPSHOT/versionrelativePath../account-parent/pom.xml/relativePath/parentartifactIdaccount-email/artifactIdpackagingjar/packagingnameAccount Email/nameurlhttp://maven.apache.org/urldependencies....../dependenciesbuildplugins....../plugins/build
/project上述的POM中使用parent元素声明父模块parent下的子元素groupId、artifactId和version指定了父模块的坐标这三个是必须元素。元素relativePath表示父模块POM的相对路径。
更新过的account-email中没有声明groupId和version不过这并不代表account-email没有groupId和version。实际上子模块隐式地从父模块继承两个元素这也就消除了一些不必要的配置。如果遇到子模块需要使用和父模块不一样的groupId或者version的情况子模块应该显示声明。
可继承的POM元素
暂时无法在飞书文档外展示此内容
依赖管理
父项目的配置信息也可以通过使用 dependencyManagement 元素来管理依赖信息。这样在子模块中使用依赖时可以不指定版本直接引用父项目中定义的版本
projectxmlnshttp://maven.apache.org/POM/4.0.0xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0http://maven.apache.org/maven-v4_0_0.xsdmodelVersion4.0.0/modelVersiongroupIdcom.kang.study.account/groupIdartifactIdaccount-parent/artifactIdversion1.0.0-SNAPSHOT/versionpackagingpom/packagingnameAccount Parent/namepropertiesspringframework.version2.5.6/springframework.versionjunit.version4.7/junit.version/propertiesdependencyManagementdependenciesdependencygroupIdorg.springframework/groupIdartifactIdspring-core/artifactIdversion${springframework.version}/version/dependencydependencygroupIdorg.springframework/groupIdartifactIdspring-beans/artifactIdversion${springframework.version}/version/dependencydependencygroupIdorg.springframework/groupIdartifactIdspring-context/artifactIdversion${springframework.version}/version/dependencydependencygroupIdorg.springframework/groupIdartifactIdspring-context-support/artifactIdversion${springframework.version}/version/dependencydependencygroupIdjunit/groupIdartifactIdjunit/artifactIdversion${junit.version}/versionscopetest/scope/dependency/dependencies/dependencyManagement
/project将springframework和junit依赖的版本以Maven变量的形式提取出来不仅消除了一些重复也使得各依赖的版本处于更加明显的位置这里使用dependencyManagement声明的依赖既不会给account-parent引入依赖也不会给它的子模块引入依赖这段配置是会被继承的。
如下是继承了dependencyManagement的account-email POM
propertiesjavax.mail.version1.4.1/javax.mail.versiongreenmail.version1.3.1b/greenmail.version
/properties
dependenciesdependencygroupIdorg.springframework/groupIdartifactIdspring-core/artifactId/dependencydependencygroupIdorg.springframework/groupIdartifactIdspring-beans/artifactId/dependencydependencygroupIdorg.springframework/groupIdartifactIdspring-context/artifactId/dependencydependencygroupIdorg.springframework/groupIdartifactIdspring-context-support/artifactId/dependencydependencygroupIdjunit/groupIdartifactIdjunit/artifactId/dependencydependencygroupIdjavax.mail/groupIdartifactIdmail/artifactIdversion${javax.mail.version}/version/dependencydependencygroupIdcom.icegreen/groupIdartifactIdgreenmail/artifactIdversion${greenmail.version}/versionscopetest/scope/dependency
/dependencies上述的POM中的依赖配置简化了些这些信息的省略是因为account-email继承了account-parent中的dependencyManagement的配置完整的依赖的声明已经包含在父POM中子模块只需要配置简单的groupId和artifactId就能获得对应的依赖信息从而引入正确的依赖。如果子模块不声明依赖的使用即使该依赖在父POM中的dependencyManagement中声明了也不会产生任何实际的效果。
依赖范围import
import范围的依赖旨在dependencyManagement元素下才有效果使用该范围的依赖通常指向一个POM作用是将目标POM中的dependencyManagement配置导入并合并到当前POM的dependencyManagement元素中。
dependencyManagementdependenciesdependencygroupIdcom.kang.study.account/groupIdartifactIdaccount-parent/artifactIdversion1.0.0-SNAPSHOT/versiontypepom/typescopeimport/scope/dependency/dependencies
/dependencyManagement上述代码中依赖的type值为pomimport范围依赖由于其特殊性一般都是指向打包类型为pom的模块。
插件管理
Maven提供了pluginManagement元素来管理插件在该元素中配置的依赖不会造成实际的插件调用行为当POM中配置了真正的plugin元素并且其groupId和artifactId与pluginManagement中配置的插件匹配时pluginManagement的配置才会影响实际的插件行为。
如下为父模块POM配置pluginManagement
buildpluginManagementpluginsplugingroupIdorg.apache.maven.plugins/groupIdartifactIdmaven-source-plugin/artifactIdversion2.1.1/versionexecutionsexecutionidattach-sources/idphaseverify/phasegoalsgoaljar-no-fork/goal/goals/execution/executions/plugin/plugins/pluginManagement
/build如下为子模块继承pluginManagement后的插件配置
buildpluginsplugingroupIdorg.apache.maven.plugins/groupIdartifactIdmaven-source-plugin/artifactId/plugin/plugins
/build聚合和继承的关系
聚合主要是为了方便快速构建项目继承主要是为了消除重复配置。在实际的项目中往往会发现一个POM既是一个聚合POM又是一个父POM所以将聚合和继承两者结合起来也没有什么问题。
例合并聚合和继承功能后的account-parent
project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0http://maven.apache.org/maven-v4_0_0.xsdmodelVersion4.0.0/modelVersiongroupIdcom.kang.study.account/groupIdartifactIdaccount-parent/artifactIdversion1.0.0-SNAPSHOT/versionpackagingpom/packagingnameAccount Parent/namemodulesmoduleaccount-email/modulemoduleaccount-persist/module/modulespropertiesspringframework.version2.5.6/springframework.versionjunit.version4.7/junit.version/propertiesdependencyManagementdependenciesdependencygroupIdorg.springframework/groupIdartifactIdspring-core/artifactIdversion${springframework.version}/version/dependencydependencygroupIdorg.springframework/groupIdartifactIdspring-beans/artifactIdversion${springframework.version}/version/dependencydependencygroupIdorg.springframework/groupIdartifactIdspring-context/artifactIdversion${springframework.version}/version/dependencydependencygroupIdorg.springframework/groupIdartifactIdspring-context-support/artifactIdversion${springframework.version}/version/dependencydependencygroupIdjunit/groupIdartifactIdjunit/artifactIdversion${junit.version}/versionscopetest/scope/dependency/dependencies/dependencyManagementbuildpluginManagementpluginsplugingroupIdorg.apache.maven.plugins/groupIdartifactIdmaven-compiler-plugin/artifactIdconfigurationsource1.8/sourcetarget1.8/target/configuration/pluginplugingroupIdorg.apache.maven.plugins/groupIdartifactIdmaven-resources-plugin/artifactIdconfigurationencodingUTF-8/encoding/configuration/plugin/plugins/pluginManagement/build
/project该POM打包方式为pom它还包含了一个modules元素表示用来聚合各个模块包含了properties、dependencyManagement和pluginManagement元素供子模块继承如果父模块在上级目录时不再需要relativePathMaven会默认识别父模块的位置。
反应堆
在一个多模块的Maven项目中反应堆是指所以模块组成的一个构建结构。对于单模块项目反应堆就是该模块本身但对于多模块项目来说反应堆就包含了各模块之间继承与依赖的关系从而能够自动计算出合理的模块构建顺序。
反应堆的构建顺序Maven按序读取POM如果该POM没有依赖模块那么就构建该模块否则就先构建其依赖模块如果该依赖还依赖于其他依赖则进一步先构建依赖的依赖。
模块间的依赖关系会将反应堆构成一个有向非循环图各个模块是该图的节点依赖关系构成了有向图这个图不允许出现循环。
使用Nexus建立私服
Nexus的仓库与仓库组
仓库的四种类型
group仓库组hosted宿主proxy代理virtual虚拟
Maven可以直接从宿主仓库下载构件Maven也可以从代理仓库下载构件而代理仓库会间接地从远程仓库下载并缓存构件Maven还可以从仓库组下载构件而仓库组没有实际内容它会转向其包含的宿主仓库或者代理仓库获得实际构件的内容。
重要仓库的介绍
Maven Central该仓库代理Maven中央仓库其策略为Release因此只会下载和缓存中央仓库中的发布版本构件Releases这是有个策略为Release的宿主类型仓库用来部署组织内部的发布版本构件Snapshots这是一个策略为Snapshot的宿主类型仓库用来部署组织内部的快照版本构件3rd party这是一个策略为Release的宿主类型仓库用来部署无法从公共仓库获得的第三方发布版本构件Public Repositories该仓库组将所有策略为Release的仓库聚合并通过一致的地址提供服务。
配置Maven从Nexus下载构件
当我们需要为项目添加Nexus私服上的public仓库时可以在POM中进行如下的配置
project......repositoriesrepositoryidnexus/idnameNexus/nameurlhttp://localhost:8081/nexus/content/groups/public//urlreleasesenabledtrue/enabled/releasessnapshotsenabledtrue/enabled/snapshots/repository/repositoriespluginRepositoriespluginRepositoryidnexus/idnameNexus/nameurlhttp://localhost:8081/nexus/content/groups/public//urlreleasesenabledtrue/enabled/releasessnapshotsenabledtrue/enabled/snapshots/pluginRepository/pluginRepositories......
/project这样的配置有个弊端只对当前Maven项目然而我们在实际的应用当中需要通过一次配置就能让本机所有的Maven项目都使用自己的Maven私服。这时我们就可以在settings.xml中进行配置
settings......profilesprofileidnexus/idrepositoriesrepositoryidnexus/idnameNexus/nameurlhttp://localhost:8081/nexus/content/groups/public//urlreleasesenabledtrue/enabled/releasessnapshotsenabledtrue/enabled/snapshots/repository/repositoriespluginRepositoriespluginRepositoryidnexus/idnameNexus/nameurlhttp://localhost:8081/nexus/content/groups/public//urlreleasesenabledtrue/enabled/releasessnapshotsenabledtrue/enabled/snapshots/pluginRepository/pluginRepositories/profile/profilesactiveProfilesactiveProfilenexus/activeProfile/activeProfiles......
/settings这个配置中使用了一个profile包含了相关的仓库配置同时配置中又使用activeProfile元素将nexus这个profile激活这样当执行Maven构建的时候激活的profile会将仓库配置应用到项目中去。
这时配置能让Maven项目从Nexus私服下载构件。但是Maven还会不时地访问中央仓库central如果希望的是所有Maven下载请求都仅仅通过Nexus以全面发挥私服的作用。可以创建一个匹配任何仓库的镜像镜像的地址为私服这样Maven对任何仓库的构件下载请求都会转到私服中。
settings
......mirrorsmirroridnexus/idmirrorOf*/mirrorOfurlhttp://localhost:8081/nexus/content/groups/public/url/mirror/mirrorsprofilesprofileidnexus/idrepositoriesrepositoryidcentral/idurlhttp://central/urlreleasesenabledtrue/enabled/releasessnapshotsenabledtrue/enabled/snapshots/repository/repositoriespluginRepositoriespluginRepositoryidcentral/idurlhttp://central/urlreleasesenabledtrue/enabled/releasessnapshotsenabledtrue/enabled/snapshots/pluginRepository/pluginRepositories/profile/profilesactiveProfilesactiveProfilenexus/activeProfile/activeProfiles
......
/settings版本管理
何为版本管理
在Maven中版本管理指的是管理项目中使用的依赖项和插件的版本号。版本管理是确保项目的稳定性、可持续性和可重复构建的重要方面之一。
版本管理中的一个关键问题是管理快照版本和发布版本之间的转换。发布版本是项目的重要里程碑它们通常是确定、稳定和可靠的版本适合于部署到生产环境中。而快照版本则是开发版本它们包含最新的代码更改但可能还没有经过完整的测试和验证。
在项目开发过程中通常需要使用快照版本来获取最新的代码更改。但是当项目达到一个阶段并准备发布时需要将快照版本转换为发布版本这个过程通常称为发布。
Maven为管理版本之间转换提供了一套严格的规则和流程。根据这些规则快照版本的构建声明周期通常是不同于发行版的。例如在Maven中快照版本将使用-SNAPSHOT作为版本后缀并使用自动增加的构建号例如1.0.0-SNAPSHOT和1.0.0-20211207-141526-1。发行版通常没有后缀例如1.0.0。
在将快照版本转换为发布版本之前需要确保项目达到了一定的稳定性和可靠性标准例如进行完整的测试和质量保证流程。一旦确定了要发布的版本需要在pom.xml文件中将版本号更新为没有后缀的版本号例如1.0.0。然后需要打包并发布这个版本。
在版本管理中还需要考虑如何处理依赖项和插件的版本更新。例如当版本转换为发布版时可能需要重新考虑依赖项和插件的版本选择并确保它们与发布版兼容。
通过合理地管理版本号可以帮助确保项目的稳定性、可维护性和可重复性。它还可以帮助团队更好地跟踪和管理依赖项的更新和升级。
Maven的版本号定义约定
Maven的版本号定义约定主版本.次版本.增量版本-里程碑版本
例1.3.4-beta-2
解读此版本号表示了该产品的第一个重大版本的第三个次要版本的第四次增量版本的beta-2里程碑。分开解释就是“1”表示了该版本是第一个重大版本“3“表示这是基于重大版本的第三个次要版本”4“表示该次要版本的第四个增量最后的”beta-2“表示该增量的某一个里程碑。
主版本表示项目的重大架构变更。次版本表示较大范围的功能增加和变化及bug修复。增量版本一般表示重大bug的修复。里程碑版本指某一个版本的里程碑。
【注意】不是每个版本号都必须拥有这四个部分。一般来说主版本和次版本都会声明但增量版本和里程碑就不一定了。
主干、标签与分支
主干项目开发代码的主体是从项目开始直到当前都处于活动的状态。从这里可以获得项目最新的源代码以及几乎所有的变更历史。
分支从主干的某个点分离出来的代码拷贝通常可以在不影响主干的前提的下在这里进行重大bug的修复或者做一些实验性质的开发。如果分支达到了预期的目的通常发生在这里的变更会被合并 merge到主干中。
标签用来标识主干或者分支的某个点的状态以代表项目的某个稳定状态这通常就是版本发布时的状态。
下面举个例子来介绍一下这些操作如何执行的
下方最长的箭头是主干项目最初的版本是1.0.0-SNAPSHOT经过一段时间的开发后1.0.0版本发布这个时候就需要打一个标签图中用一个长图表示。然后项目进入1.1.0-SNAPSHOT状态大量的开发工作都完成在主干中添加了一些新特性并修复了很多bug之后项目1.0.0发布同样这时候需要打另一种标签。发布过后项目进入1.2.0-SNAPSHOT可这个时候用户报告1.1.0版本有一个重大的bug需要尽快修复我们不能在主干中修复bug因为主干有大多的变化无法在短时间内测试完毕并发布我们也不能停止1.2.0-SNAPSHOT的开发因此这时候可以基于1.1.0创建一个1.1.1-SNAPSHOT的分支在这里进行bug修复然后为用户发布一个1.1.1增量版本同时打上标签。当然还不能忘了把bug修复涉及的变更合并到1.2.0-SNAPSHOT的主干中。主干在开发一段时间之后发布1.2.0版本然后进入到新版本1.3.0-SNAPSHOT的开发过程中。
Maven属性
使用Maven属性归类依赖是最常见的使用Maven属性的方式通过properties元素用户可以自定义一个或多个Maven属性然后在POM的其他地方使用${属性名称}的方式引用该属性这种做法的最大意义在于消除重复。这样不仅减少了日后升级版本的工作量也能降低错误发生的效率。
Maven属性共有6类 内置属性 主要有两个常用的内置属性 b a s e d i r 表示项目根目录即包含 p o m . x m l 文件的目录 {basedir}表示项目根目录即包含pom.xml文件的目录 basedir表示项目根目录即包含pom.xml文件的目录{version}表示项目的版本。 POM属性 用户可以使用该类属性引用POM文件中对应元素的值。例如${project.artifactId}就对应了projectartifactId元素的值常用的POM属性包括 ${project.build.sourceDirectory}项目的主源码目录默认为src/main/java/。${project.build.testSourceDirectory}项目的测试源码目录默认为src/test/java/。${project.build.directory}项目构建输出目录默认为target/。${project.outputDirectory}项目主代码编译输出目录默认为target/classes/。${project.testOutputDirectory}项目测试代码编译输出目录默认为target/test-classes/。${project.groupId}项目的groupId。${project.artifactId}项目的artifactId。 p r o j e c t . v e r s i o n 项目的 v e r s i o n 与 {project.version}项目的version与 project.version项目的version与{version}等价。 p r o j e c t . b u i l d . f i n a l N a m e 项目打包输出文件的名称默认为 {project.build.finalName}项目打包输出文件的名称默认为 project.build.finalName项目打包输出文件的名称默认为{project.artifactId}-${project.version}。 自定义属性 用户可以在POM的properties元素下自定义Maven属性。 project......propertiesmy.prophello/my.prop/properties......
/project然后在POM中其他地方使用${my.prop}的时候会被替换为hello。 Settings属性 与POM属性同理用户使用以settings.开头的属性引用settings.xml文件中的XML元素的值如常用的${settings.localRepository}指向用户本地仓库的地址。 Java系统属性 所有Java系统属性都可以使用Maven属性引用例如${user.home}指向了用户目录。 环境变量属性 所有环境变量都可以使用env.开头的Maven属性引用。例如${env.JAVA_HOME}指代了JAVA_HOME环境变量的值。