一、scope
-
compile (v. 编译): 全流程都在;
-
test: 比如 junit, 只出现在测试中;(仅测试)
-
provided: 比如 servlet_api, 出现在编译时需要, 运行时由环境提供 (容器已有), 不打进 war 包, 最终都有包用; (租用不买)
-
runtime: 比如 jdbc, devtools, 与compile类似,都会打进 war 包。出现的目的主要是环境需要,但写代码用不到 (购入不用) 就像项目投标需要资格证书。与 provide 区别主要如下:
能否 import | 编译 | run | 出现在 war 包中 | |
---|---|---|---|---|
compile | 1 | 1 | 1 | 1 |
runtime | 1 | 1 | ||
provided | 1 | 1 |
-
除了
provided
,其他的两个scope
会传递(transitive)到子pom
。 -
常见的 scope 例子:
# tomcat 容器中也有servlet-api包,本地就不用打进war/jar包
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<scope>provided</scope>
</dependency>
# 环境需要
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
- system:引用本地的jar包才会选择这个 scope
<dependency>
<groupId>java.test</groupId>
<artifactId>java.test-api</artifactId>
<version>3.1.0</version>
<systemPath>本地文件路径</systemPath>
<scope>system</scope>
</dependency>
二、optional
<optional>
标签为 true 时(默认为 false),代表对继承了该坐标的子项目是可选特性。也就是不会被继承(好比 Java 中的 final 类不能被其他类继承一样)
<scope>provided</scope>
和<optional>true</optional>
都不会传递到子pom
中
optional 与 provided 的区别
<optional>true</optional>
和<scope>provided</scope>
的区别:
使用 provided 的范围添加依赖项, 在逻辑上与使用 optional 标志添加依赖项不同. provided 的依赖将由我们模块的运行时环境提供, 在这种情况下没有可选功能. 请注意, 子项目再次添加了该依赖项, 范围也一定是 provider 的, 因为我们知道它将由项目的最终运行时环境提供
-
optional 表示依赖可选,该依赖是否使用都不会影响服务运行
-
provided 表示依赖必须但通常是由最终运行时 (系统或者容器) 提供, 不需要添加到 jar 包
optional: 吃面时的酱油, 不加也不会影响 (注意, 如果去掉影响子项目, 则表示不适合用 optional )
provided: 吃面时的筷子, 这样的东西都是必须的, 不过一般店家给顾客备好了, 不需要顾客自带
- 常见的 optional 例子:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
我的理解是, 他们都起一样的效果仅在语义上有所区别.
-
optional
: 如果我提供了4种数据库的访问方式, 但实际使用只需要其中一种 (将选择交给别人来决定) , 比如spring-boot-configuration-processor
-
provided
: 如果这个功能只是起辅助作用, 并不会给别人用. 比如说lombok
类似于这些插件, 他们生成代码和配置后, 就不再使用.
三、DependencyManagement
- <dependencyManagement> 预定义了依赖版本,在 dependency 使用时就可以不用 scope 和 version;
- 子模块引用父类 pom 的 <dependencyManagement> 必须设置 scope 为 import;
- dependencyManagement,只在被 dependency 使用时才会导包。这种只引入需要的依赖方式也称为 “按需继承”;
四、scope 为 import
import
This scope is only supported on a dependency of type pom in the section. It indicates the dependency to be replaced with the effective list of dependencies in the specified POM’s
<dependencyManagement> section. Since they are replaced, dependencies with a scope of import do not actually participate in limiting the transitivity of a dependency.
- 当导入的模块没有 <scope>import</scope> 时,会将父模块所有的
普通dependency
全部继承到当前工程 dependencyManagement 中,并且会传递依赖。 - 有 <scope>import</scope> 继承的是 dependencyManagement;
- 没有 <scope>import</scope> 继承的是 dependency(也称为普通 dependency);
- 注意:在 dependencyManagement 中,scope = import 只能用于 type = pom 的 dependency 里;
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
- 大家使用 ide 点进 artifact 里面,看依赖内容是写在那里的就行了。写在 management 里面就加一条 import,如果是写在 dependency 里面,就不加,最终都会成为本 pom 的 dependencyManagement。
五、type
- <dependency>中 type 默认为 jar,即引入一个 jar 包。因为 pom 中大量的 jar 包会造成导的包过多过大,显得特别混乱。所以设置 type 为 pom,maven 就会将里面的所有的 jar 包打包成一个 pom,然后依赖 pom(即含有所有依赖的jar包)
- 当 type 不为 jar 时,必须写上 <type> 标签;
- pom 对应
<packaging>pom<packaging>
; - type 还有其他类型,比如 war、bar、apk、ejb 等,仅供了解。
六、插件
-
mvn 命令本身不会执行任何功能,实际干活的是插件;
-
maven 并不会直接调用插件;
-
maven 是通过 pom 文件里的
<plugin>
标签,绑定插件的坐标,再根据里面的<execution>
定义的 goals 和 phase 来干活的; -
n:n关系:插件和 Maven
- 插件里可以有多个 goal
- maven.lifecycle.phase 可以绑定一个或多个 goal
-
插件的功能是通过 maven 的生命周期(lifecycle)顺序执行周期(phase)触发周期绑定的插件里的 goal 来实现的;
-
Maven 会根据
packaging
默认绑定了一些自带的插件,无显示需引用;比如执行 mvn clean 会执行 maven-clean-plugin 插件下的 goal: clean。 -
当
packaging
为 jar 时(不写默认为 jar ),执行mvn package 才默认绑定具体打包插件。 -
深入了解可以查看我的另一篇文章:如何编写 Maven 插件
七、Goal
执行的Phase | 对应执行的Goal |
---|---|
compile | compiler:compile |
test | compiler:testCompile surefire:test |
- 使用方法:
mvn [plugin-name]:[goal-name]
e.g.mvn compiler:compile
- SpringBoot 打包插件会把自己绑在 package 这一步了,Maven 执行到 package 这一步就会自动调用这个插件;(之所以没有写 execution 是因为,继承了 parent :spring-boot-parent,在父 pom 中,已经默认定义了)
八、生命周期
- maven的有非常多生命周期,如pre-clean、clean…到site、post-site、site-deploy等,但实际使用的是少数几个周期;
- 这些周期(phase)多个一组,分为三套生命周期(每套lifecycle下面都有一些细分的phase:pre-、post-)
- clean 周期:清理项目(“clean周期”是一套,包含“clean” phase)
- default 周期:主体周期,构建…
- site 周期:建立站点,部署到服务器
从实际使用角度理解(实际的作用):
- 火车(lifecycle)、车厢(phase)、货品(goal),货品(goal)可以放在任何一列火车(lifecycle)的任何车厢(phase),一列火车(lifecycle)包含多节车厢(phase),车厢(phase)是有顺序的,货品按照从前往后摆放(绑定)顺序。
- 火车(lifecycle)只有三辆:clean、default、site
- 执行mvn clean package 执行的是 phase,对比下表实际发生的就是让clean火车(lifecycle)开到clean(phase),执行了两步:pre-clean、clean;然后让default火车(lifecycle)开到package(phase),执行了n步;
-
详细的生命周期 lifecycle(顺序)
- clean(3个):
pre-clean, clean, post-clean - default(23个):
validate, initialize, generate-sources, process-sources, generate-resources, process-resources, compile, process-classes, generate-test-sources, process-test-sources, generate-test-resources, process-test-resources, test-compile, process-test-classes, test, prepare-package, package, pre-integration-test, integration-test, post-integration-test, verify, install, deploy - site(4个):
pre-site, site, post-site, site-deploy
- clean(3个):
-
maven 命令
mvn [options] [<goal(s)>] [<phase(s)>]
- The typical invocation for building a Maven project uses a Maven life cycle phase. E.g.
mvn verify
九、properties
可以自定义一个或多个 maven 属性,然后在 pom.xml 里使用 ${属性名} 引用。
maven 总共有 6 类属性:内置属性、POM属性、自定义属性、Settings属性、java系统属性和环境变量属性
- 内置属性(两个常用内置属性)
${basedir}
表示项目跟目录,即包含 pom.xml 文件的目录;
${version}
表示项目版本
- pom 属性
用户可以使用该类属性引用POM文件中对应元素的值。如${project.artifactId}
就对应了<project>
<artifactId>
元素的值,常用的POM属性包括:
${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.groupId}:项目的groupId
${project.artifactId}:项目的artifactId
${project.version}:项目的version,与${version} 等价
${project.build.finalName}:项目打包输出文件的名称,默认为${project.artifactId}-${project.version}
- 自定义属性
最好理解的属性;
- settings属性
与 pom 属性同理,用户使用以 settings. 开头的属性引用 settings.xml 文件中的 XML 元素的值
- Java系统属性
所有 java 系统属性都可以用 maven 属性引用,如 ${user.home}
指向了用户目录
- 环境变量属性
所有环境变量属性都可以使用以 env. 开头的 maven 属性引用,如 ${env.JAVA_HOME}
指代了 JAVA_HOME 环境变量的的值
十、聚合(包含)与继承的关系
-
继承不等于聚合(包含)
-
聚合主要是为了方便快速构建项目,继承主要是为了消除重复配置
-
对于聚合模块而言,它知道有哪些被聚合的模块,但那些被聚合的模块不知道这个聚合模块的存在;对于继承的父pom 而言,它不知道有哪些子模块继承它,但那些子模块都必须知道自己的父pom 是什么
-
聚合pom 与继承中的父pom 的 packaging 都必须是 pom;同时,聚合模块与继承中的父模块除了 pom 外,都没有实际的内容
-
插件的传播 (inherited) 与继承
- POM 中的任何元素都会被继承
- 插件也不列外,但可以重写子模块插件的
configuration
为skip
等于true
,表示在本模块中跳过执行该插件。 - 无法中止继承,但插件的配置信息可以中止传播或者使用 pluginManagement 然后手动调用
- 设置本插件跳过执行
<build>
<plugins>
<plugin>
<g>..<a>..<v>..
<configuration>
<skip>true</skip>
</configuration>
- 中止传播配置
<build>
<plugins>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<version>1.4.10</version>
<executions>
<execution>
<!--配置不会被传播到子POM -->
<inherited>false</inherited>
<id>default</id>
<goals>
<goal>build</goal>
<goal>push</goal>
</goals>
</execution>
</executions>
<!--任何配置 都不会被传播到子项目 (不填默认为true, 默认为传播) -->
<inherited>false</inherited>
<configuration>
<skip>false</skip>
<repository>10.8.206.107:5000/${project.artifactId}</repository>
</configuration>
</plugin>
</plugins>
</build>
- 如同可以设置 -Dmaven.test.skip=true ,我们也可以在不想继承的父模块内加上
<插件名.skip>true</>
,然后在configuration里面设置skip为false:
<properties>
<!--这里 dockerfile 是插件名称-->
<dockerfile.skip>true</dockerfile.skip>
</properties>
<build>
<plugins>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<version>1.4.10</version>
<configuration>
<!--加上本模块跳过为false-->
<skip>false</skip>
<repository>10.8.206.107:5000/${project.artifactId}</repository>
</configuration>
</plugin>
</plugins>
</build>
十一、POM 的四层体系
超级POM - 父POM - 当前POM - 有效POM (effective pom)
-
超级POM的位置:
${MAVEN_HOME}/lib/maven-model-builder-3.0.4.jar
,打开该文件,能找到超级POM:\org\apache\maven\model\pom-4.0.0.xml,它是所有Maven POM的父POM,所有Maven项目继承该配置。 -
查看当前有效pom的命令:
mvn help:effective-pom
-
查看当前项目的所有mvn配置
mvn -X
-
打印所有可用的环境变量和Java系统属性
mvn help:system
-
生成新pom命令:
mvn archetype:generate
-
查看依赖树:
mvn dependency:tree
-
Maven 有哪些元素(一层)
- groupId
- version
- description
- organization:组织信息;
- inceptionYear:项目的创始年份;
- url :项目的 url 地址
- develoers:项目的开发者信息;
- contributors:项目的贡献者信息;
- distributionManagerment:项目的部署信息;
- issueManagement:缺陷跟踪系统信息;
- ciManagement:项目的持续继承信息;
- scm:项目的版本控制信息;
- mailingLists:项目的邮件列表信息;
- properties:自定义的 Maven 属性;
- dependencies:项目的依赖配置;
- dependencyManagement:醒目的依赖管理配置;
- repositories:项目的仓库配置;
- build:包括项目的源码目录配置、输出目录配置、插件配置、插件管理配置等;
- reporting:包括项目的报告输出目录配置、报告插件配置等。
十二、版本仲裁
同级先声明用谁,不同级谁近用谁。