视频连接
资料连接
在入门的时候我们介绍说 Maven 是一款『构建管理』和『依赖管理』的工具。但事实上这只是 Maven 的一部分功能。Maven 本身的产品定位是一款『项目管理工具』。
下面是 spring-boot-starter
的 POM 文件,可以看到:除了我们熟悉的坐标标签、dependencies
标签,还有 description
、url
、organization
、licenses
、developers
、scm
、issueManagement
等这些描述项目信息的标签。
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0modelVersion>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
<version>2.5.6version>
<name>spring-boot-startername>
<description>Core starter, including auto-configuration support, logging and YAMLdescription>
<url>https://spring.io/projects/spring-booturl>
<organization>
<name>Pivotal Software, Inc.name>
<url>https://spring.iourl>
organization>
<licenses>
<license>
<name>Apache License, Version 2.0name>
<url>https://www.apache.org/licenses/LICENSE-2.0url>
license>
licenses>
<developers>
<developer>
<name>Pivotalname>
<email>[email protected]email>
<organization>Pivotal Software, Inc.organization>
<organizationUrl>https://www.spring.ioorganizationUrl>
developer>
developers>
<scm>
<connection>scm:git:git://github.com/spring-projects/spring-boot.gitconnection>
<developerConnection>scm:git:ssh://[email protected]/spring-projects/spring-boot.gitdeveloperConnection>
<url>https://github.com/spring-projects/spring-booturl>
scm>
<issueManagement>
<system>GitHubsystem>
<url>https://github.com/spring-projects/spring-boot/issuesurl>
issueManagement>
<dependencies>
<dependency>
……
dependency>
dependencies>
project>
所以从『项目管理』的角度来看,Maven 提供了如下这些功能:
项目对象模型(POM):将整个项目本身抽象、封装为应用程序中的一个对象,以便于管理和操作。
全局性构建逻辑重用:Maven 对整个构建过程进行封装之后,程序员只需要指定配置信息即可完成构建。让构建过程从 Ant 的『编程式』升级到了 Maven 的『声明式』。
构件的标准集合:在 Maven 提供的标准框架体系内,所有的构件都可以按照统一的规范生成和使用。
构件关系定义:Maven 定义了构件之间的三种基本关系,让大型应用系统可以使用 Maven 来进行管理
插件目标系统:Maven 核心程序定义抽象的生命周期,然后将插件的目标绑定到生命周期中的特定阶段,实现了标准和具体实现解耦合,让 Maven 程序极具扩展性
项目描述信息的维护:我们不仅可以在 POM 中声明项目描述信息,更可以将整个项目相关信息收集起来生成 HTML 页面组成的一个可以直接访问的站点。这些项目描述信息包括:
经过我们前面的学习,我们看到 Maven 在构建过程中有很多默认的设定。例如:源文件存放的目录、测试源文件存放的目录、构建输出的目录……等等。但是其实这些要素也都是被 Maven 定义过的。定义的位置就是:超级 POM。
关于超级 POM,Maven 官网是这样介绍的:
The Super POM is Maven’s default POM. All POMs extend the Super POM unless explicitly set, meaning the configuration specified in the Super POM is inherited by the POMs you created for your projects.
译文:Super POM 是 Maven 的默认 POM。除非明确设置,否则所有 POM 都扩展 Super POM,这意味着 Super POM 中指定的配置由您为项目创建的 POM 继承。
所以我们自己的 POM 即使没有明确指定一个父工程(父 POM),其实也默认继承了超级 POM。就好比一个 Java 类默认继承了 Object 类。
那么超级 POM 中定义了哪些东西呢?如下:
<project>
<modelVersion>4.0.0modelVersion>
<repositories>
<repository>
<id>centralid>
<name>Central Repositoryname>
<url>https://repo.maven.apache.org/maven2url>
<layout>defaultlayout>
<snapshots>
<enabled>falseenabled>
snapshots>
repository>
repositories>
<pluginRepositories>
<pluginRepository>
<id>centralid>
<name>Central Repositoryname>
<url>https://repo.maven.apache.org/maven2url>
<layout>defaultlayout>
<snapshots>
<enabled>falseenabled>
snapshots>
<releases>
<updatePolicy>neverupdatePolicy>
releases>
pluginRepository>
pluginRepositories>
<build>
<directory>${project.basedir}/targetdirectory>
<outputDirectory>${project.build.directory}/classesoutputDirectory>
<finalName>${project.artifactId}-${project.version}finalName>
<testOutputDirectory>${project.build.directory}/test-classestestOutputDirectory>
<sourceDirectory>${project.basedir}/src/main/javasourceDirectory>
<scriptSourceDirectory>${project.basedir}/src/main/scriptsscriptSourceDirectory>
<testSourceDirectory>${project.basedir}/src/test/javatestSourceDirectory>
<resources>
<resource>
<directory>${project.basedir}/src/main/resourcesdirectory>
resource>
resources>
<testResources>
<testResource>
<directory>${project.basedir}/src/test/resourcesdirectory>
testResource>
testResources>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-antrun-pluginartifactId>
<version>1.3version>
plugin>
<plugin>
<artifactId>maven-assembly-pluginartifactId>
<version>2.2-beta-5version>
plugin>
<plugin>
<artifactId>maven-dependency-pluginartifactId>
<version>2.8version>
plugin>
<plugin>
<artifactId>maven-release-pluginartifactId>
<version>2.5.3version>
plugin>
plugins>
pluginManagement>
build>
<reporting>
<outputDirectory>${project.build.directory}/siteoutputDirectory>
reporting>
<profiles>
<profile>
<id>release-profileid>
<activation>
<property>
<name>performReleasename>
<value>truevalue>
property>
activation>
<build>
<plugins>
<plugin>
<inherited>trueinherited>
<artifactId>maven-source-pluginartifactId>
<executions>
<execution>
<id>attach-sourcesid>
<goals>
<goal>jar-no-forkgoal>
goals>
execution>
executions>
plugin>
<plugin>
<inherited>trueinherited>
<artifactId>maven-javadoc-pluginartifactId>
<executions>
<execution>
<id>attach-javadocsid>
<goals>
<goal>jargoal>
goals>
execution>
executions>
plugin>
<plugin>
<inherited>trueinherited>
<artifactId>maven-deploy-pluginartifactId>
<configuration>
<updateReleaseInfo>trueupdateReleaseInfo>
configuration>
plugin>
plugins>
build>
profile>
profiles>
project>
和 Java 类一样,POM 之间其实也是单继承的。如果我们给一个 POM 指定了父 POM,那么继承关系如下图所示:
有效 POM 英文翻译为 effective POM,它的概念是这样的——在 POM 的继承关系中,子 POM 可以覆盖父 POM 中的配置;如果子 POM 没有覆盖,那么父 POM 中的配置将会被继承。按照这个规则,继承关系中的所有 POM 叠加到一起,就得到了一个最终生效的 POM。显然 Maven 实际运行过程中,执行构建操作就是按照这个最终生效的 POM 来运行的。这个最终生效的 POM 就是有效 POM,英文叫effective POM。
mvn help:effective-pom
综上所述,平时我们使用和配置的 POM 其实大致是由四个层次组成的:
官网说明地址:https://maven.apache.org/plugins/maven-help-plugin
目标 | 说明 |
---|---|
help:active-profiles | 列出当前已激活的 profile |
help:all-profiles | 列出当前工程所有可用 profile |
help:describe | 描述一个插件和/或 Mojo 的属性 |
help:effective-pom | 以 XML 格式展示有效 POM |
help:effective-settings | 为当前工程以 XML 格式展示计算得到的 settings 配置 |
help:evaluate |
计算用户在交互模式下给出的 Maven 表达式 |
help:system | 显示平台详细信息列表,如系统属性和环境变量 |
<properties>
<com.atguigu.hello>good morning mavencom.atguigu.hello>
properties>
mvn help:evaluate
Properties properties = System.getProperties();
Set<Object> propNameSet = properties.keySet();
for (Object propName : propNameSet) {
String propValue = properties.getProperty((String) propName);
System.out.println(propName + " = " + propValue);
}
java.runtime.name = Java™ SE Runtime Environment
sun.boot.library.path = D:\software\Java\jre\bin
java.vm.version = 25.141-b15
java.vm.vendor = Oracle Corporation
java.vendor.url = http://java.oracle.com/
path.separator = ;
java.vm.name = Java HotSpot™ 64-Bit Server VM
file.encoding.pkg = sun.io
user.country = CN
user.script =
sun.java.launcher = SUN_STANDARD
sun.os.patch.level =
java.vm.specification.name = Java Virtual Machine Specification
user.dir = D:\idea2019workspace\atguigu-maven-test-prepare
java.runtime.version = 1.8.0_141-b15
java.awt.graphicsenv = sun.awt.Win32GraphicsEnvironment
java.endorsed.dirs = D:\software\Java\jre\lib\endorsed
os.arch = amd64
java.io.tmpdir = C:\Users\ADMINI~1\AppData\Local\Temp\
line.separator =
java.vm.specification.vendor = Oracle Corporation
user.variant =
os.name = Windows 10
sun.jnu.encoding = GBK
java.library.path = D:\software\Java\bin;C:\WINDOWS\Sun\Java\bin;C:\WIN……
java.specification.name = Java Platform API Specification
java.class.version = 52.0
sun.management.compiler = HotSpot 64-Bit Tiered Compilers
os.version = 10.0
user.home = C:\Users\Administrator
user.timezone =
java.awt.printerjob = sun.awt.windows.WPrinterJob
file.encoding = UTF-8
java.specification.version = 1.8
java.class.path = D:\software\Java\jre\lib\charsets.jar;D:\softw……
user.name = Administrator
java.vm.specification.version = 1.8
sun.java.command = com.atguigu.maven.MyTest
java.home = D:\software\Java\jre
sun.arch.data.model = 64
user.language = zh
java.specification.vendor = Oracle Corporation
awt.toolkit = sun.awt.windows.WToolkit
java.vm.info = mixed mode
java.version = 1.8.0_141
java.ext.dirs = D:\software\Java\jre\lib\ext;C:\WINDOWS\Sun\Java\lib\ext
sun.boot.class.path = D:\software\Java\jre\lib\resources.jar;D:\sof……
java.vendor = Oracle Corporation
file.separator = \
java.vendor.url.bug = http://bugreport.sun.com/bugreport/
sun.io.unicode.encoding = UnicodeLittle
sun.cpu.endian = little
sun.desktop = windows
sun.cpu.isalist = amd64
${env.系统环境变量名}
使用表达式 ${project.xxx}
可以访问当前 POM 中的元素值。
${project.标签名}
${project.标签名.子标签名}
${project.标签名[下标]}
${settings.标签名}
可以访问 settings.xml
中配置的元素值。
pom.xml
文件中引用属性在实际使用 Maven 的过程中,我们会发现 build
标签有时候有,有时候没,这是怎么回事呢?其实通过有效 POM 我们能够看到,build
标签的相关配置其实一直都在,只是在我们需要定制构建过程的时候才会通过配置 build
标签覆盖默认值或补充配置。这一点我们可以通过打印有效 POM 来看到。
build
标签的完整示例:
<build>
<sourceDirectory>D:\idea2019workspace\atguigu-maven-test-prepare\src\main\javasourceDirectory>
<scriptSourceDirectory>D:\idea2019workspace\atguigu-maven-test-prepare\src\main\scriptsscriptSourceDirectory>
<testSourceDirectory>D:\idea2019workspace\atguigu-maven-test-prepare\src\test\javatestSourceDirectory>
<outputDirectory>D:\idea2019workspace\atguigu-maven-test-prepare\target\classesoutputDirectory>
<testOutputDirectory>D:\idea2019workspace\atguigu-maven-test-prepare\target\test-classestestOutputDirectory>
<resources>
<resource>
<directory>D:\idea2019workspace\atguigu-maven-test-prepare\src\main\resourcesdirectory>
resource>
resources>
<testResources>
<testResource>
<directory>D:\idea2019workspace\atguigu-maven-test-prepare\src\test\resourcesdirectory>
testResource>
testResources>
<directory>D:\idea2019workspace\atguigu-maven-test-prepare\targetdirectory>
<finalName>atguigu-maven-test-prepare-1.0-SNAPSHOTfinalName>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-antrun-pluginartifactId>
<version>1.3version>
plugin>
<plugin>
<artifactId>maven-assembly-pluginartifactId>
<version>2.2-beta-5version>
plugin>
<plugin>
<artifactId>maven-dependency-pluginartifactId>
<version>2.8version>
plugin>
<plugin>
<artifactId>maven-release-pluginartifactId>
<version>2.5.3version>
plugin>
plugins>
pluginManagement>
<plugins>
<plugin>
<artifactId>maven-clean-pluginartifactId>
<version>2.5version>
<executions>
<execution>
<id>default-cleanid>
<phase>cleanphase>
<goals>
<goal>cleangoal>
goals>
execution>
executions>
plugin>
<plugin>
<artifactId>maven-resources-pluginartifactId>
<version>2.6version>
<executions>
<execution>
<id>default-testResourcesid>
<phase>process-test-resourcesphase>
<goals>
<goal>testResourcesgoal>
goals>
execution>
<execution>
<id>default-resourcesid>
<phase>process-resourcesphase>
<goals>
<goal>resourcesgoal>
goals>
execution>
executions>
plugin>
<plugin>
<artifactId>maven-jar-pluginartifactId>
<version>2.4version>
<executions>
<execution>
<id>default-jarid>
<phase>packagephase>
<goals>
<goal>jargoal>
goals>
execution>
executions>
plugin>
<plugin>
<artifactId>maven-compiler-pluginartifactId>
<version>3.1version>
<executions>
<execution>
<id>default-compileid>
<phase>compilephase>
<goals>
<goal>compilegoal>
goals>
execution>
<execution>
<id>default-testCompileid>
<phase>test-compilephase>
<goals>
<goal>testCompilegoal>
goals>
execution>
executions>
plugin>
<plugin>
<artifactId>maven-surefire-pluginartifactId>
<version>2.12.4version>
<executions>
<execution>
<id>default-testid>
<phase>testphase>
<goals>
<goal>testgoal>
goals>
execution>
executions>
plugin>
<plugin>
<artifactId>maven-install-pluginartifactId>
<version>2.4version>
<executions>
<execution>
<id>default-installid>
<phase>installphase>
<goals>
<goal>installgoal>
goals>
execution>
executions>
plugin>
<plugin>
<artifactId>maven-deploy-pluginartifactId>
<version>2.7version>
<executions>
<execution>
<id>default-deployid>
<phase>deployphase>
<goals>
<goal>deploygoal>
goals>
execution>
executions>
plugin>
<plugin>
<artifactId>maven-site-pluginartifactId>
<version>3.3version>
<executions>
<execution>
<id>default-siteid>
<phase>sitephase>
<goals>
<goal>sitegoal>
goals>
<configuration>
<outputDirectory>D:\idea2019workspace\atguigu-maven-test-prepare\target\siteoutputDirectory>
<reportPlugins>
<reportPlugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-project-info-reports-pluginartifactId>
reportPlugin>
reportPlugins>
configuration>
execution>
<execution>
<id>default-deployid>
<phase>site-deployphase>
<goals>
<goal>deploygoal>
goals>
<configuration>
<outputDirectory>D:\idea2019workspace\atguigu-maven-test-prepare\target\siteoutputDirectory>
<reportPlugins>
<reportPlugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-project-info-reports-pluginartifactId>
reportPlugin>
reportPlugins>
configuration>
execution>
executions>
<configuration>
<outputDirectory>D:\idea2019workspace\atguigu-maven-test-prepare\target\siteoutputDirectory>
<reportPlugins>
<reportPlugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-project-info-reports-pluginartifactId>
reportPlugin>
reportPlugins>
configuration>
plugin>
plugins>
build>
所以本质上来说:我们配置的 build
标签都是对超级 POM 配置的叠加。那我们又为什么要在默认配置的基础上叠加呢?很简单,在默认配置无法满足需求的时候定制构建过程。
从完整示例中我们能够看到,build
标签的子标签大致包含三个主体部分:
参考示例中的如下部分:
<sourceDirectory>D:\idea2019workspace\atguigu-maven-test-prepare\src\main\javasourceDirectory>
<scriptSourceDirectory>D:\idea2019workspace\atguigu-maven-test-prepare\src\main\scriptsscriptSourceDirectory>
<testSourceDirectory>D:\idea2019workspace\atguigu-maven-test-prepare\src\test\javatestSourceDirectory>
<outputDirectory>D:\idea2019workspace\atguigu-maven-test-prepare\target\classesoutputDirectory>
<testOutputDirectory>D:\idea2019workspace\atguigu-maven-test-prepare\target\test-classestestOutputDirectory>
<resources>
<resource>
<directory>D:\idea2019workspace\atguigu-maven-test-prepare\src\main\resourcesdirectory>
resource>
resources>
<testResources>
<testResource>
<directory>D:\idea2019workspace\atguigu-maven-test-prepare\src\test\resourcesdirectory>
testResource>
testResources>
<directory>D:\idea2019workspace\atguigu-maven-test-prepare\targetdirectory>
我们能看到各个目录的作用如下:
目录名 | 作用 |
---|---|
sourceDirectory | 主体源程序存放目录 |
scriptSourceDirectory | 脚本源程序存放目录 |
testSourceDirectory | 测试源程序存放目录 |
outputDirectory | 主体源程序编译结果输出目录 |
testOutputDirectory | 测试源程序编译结果输出目录 |
resources | 主体资源文件存放目录 |
testResources | 测试资源文件存放目录 |
directory | 构建结果输出目录 |
pluginManagement
标签存放着几个极少用到的插件:
maven-antrun-plugin
maven-assembly-plugin
maven-dependency-plugin
maven-release-plugin
通过 pluginManagement
标签管理起来的插件就像 dependencyManagement
一样,子工程使用时可以省略版本号,起到在父工程中统一管理版本的效果。情看下面例子:
被 spring-boot-dependencies
管理的插件信息:
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<version>2.6.2version>
plugin>
子工程使用的插件信息:
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
plugins
标签存放的是默认生命周期中实际会用到的插件,这些插件想必大家都不陌生,所以抛开插件本身不谈,我们来看看 plugin
标签的结构:
<plugin>
<artifactId>maven-compiler-pluginartifactId>
<version>3.1version>
<executions>
<execution>
<id>default-compileid>
<phase>compilephase>
<goals>
<goal>compilegoal>
goals>
execution>
<execution>
<id>default-testCompileid>
<phase>test-compilephase>
<goals>
<goal>testCompilegoal>
goals>
execution>
executions>
plugin>
artifactId
和 version
标签定义了插件的坐标,作为 Maven 的自带插件这里省略了 groupId
。
executions
标签内可以配置多个execution
标签,execution
标签内:
id
:指定唯一标识phase
:关联的生命周期阶段goals/goal
:关联指定生命周期的目标
goals
标签中可以配置多个 goal
标签,表示一个生命周期环节可以对应当前插件的多个目标。另外,插件目标的执行过程可以进行配置,例如 maven-site-plugin
插件的 site
目标:
<execution>
<id>default-siteid>
<phase>sitephase>
<goals>
<goal>sitegoal>
goals>
<configuration>
<outputDirectory>D:\idea2019workspace\atguigu-maven-test-prepare\target\siteoutputDirectory>
<reportPlugins>
<reportPlugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-project-info-reports-pluginartifactId>
reportPlugin>
reportPlugins>
configuration>
execution>
configuration
标签内进行配置时使用的标签是插件本身定义的。就以 maven-site-plugin
插件为例,它的核心类是 org.apache.maven.plugins.site.render.SiteMojo
,在这个类中我们看到了 outputDirectory
属性:
SiteMojo
的父类是:AbstractSiteRenderingMojo
,在父类中我们看到 reportPlugins
属性:
结论:每个插件能够做哪些设置都是各个插件自己规定的,无法一概而论。
前面我们在settings.xml
中配置了 JDK 版本,那么将来把 Maven 工程部署都服务器上,脱离了 settings.xml
配置,如何保证程序正常运行呢?思路就是我们直接把 JDK 版本信息告诉负责编译操作的 maven-compiler-plugin
插件,让它在构建过程中,按照我们指定的信息工作。
为了测试对 maven-compiler-plugin
插件进行配置的效果,我们暂时取消 settings.xml
中的 profile
配置。
很明显这里用到了 Lambda 表达式,这是 JDK 1.8 才支持的语法。
package com.atguigu.maven;
public class Hello {
public void hello() {
new Thread(()->{
System.out.println("thread ...");
}).start();
}
}
此时我们执行编译命令:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<version>3.1version>
<configuration>
<source>1.8source>
<target>1.8target>
<encoding>UTF-8encoding>
configuration>
plugin>
plugins>
build>
settings.xml
中配置:仅在本地生效,如果脱离当前 settings.xml
能够覆盖的范围,则无法生效。pom.xml
中配置:无论在哪个环境执行编译等构建操作都有效。查看 Maven 官网页面 ,我们找到 source
标签的介绍:
翻译过来就是:调用 Java 编译器命令时传入的 -source
参数。那对编译器来说,-source
参数是啥意思呢?
『提供与指定发行版的源兼容性』这句话我的理解是:
另外我们还看到:
这个功能还可以通过在 properties
标签中配置 maven.compiler.source
属性来实现。所以我们也经常会看到类似这样的配置:
<properties>
<maven.compiler.source>1.8maven.compiler.source>
<maven.compiler.target>1.8maven.compiler.target>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
properties>
翻译过来就是:调用 Java 编译器命令时传入的 -target
参数。那对编译器来说,-target
参数是啥意思呢?
『生成特定 VM 版本的类文件』这句话我的理解是:
*.class
字节码文件*.class
字节码文件要符合指定的 JVM 版本很显然 spring-boot-maven-plugin
并不是 Maven 自带的插件,而是 SpringBoot 提供的,用来改变 Maven 默认的构建行为。具体来说是改变打包的行为。默认情况下 Maven 调用 maven-jar-plugin
插件的 jar
目标,生成普通的 jar
包。
普通 jar
包没法使用 java -jar xxx.jar
这样的命令来启动、运行,但是 SpringBoot 的设计理念就是每一个『微服务』导出为一个 jar
包,这个jar
包可以使用 java -jar xxx.jar
这样的命令直接启动运行。
这样一来,打包的方式肯定要进行调整。所以 SpringBoot 提供了 spring-boot-maven-plugin
这个插件来定制打包行为。
所有的一切已经都被 SpringBoot 封装好了,所以配置非常简单,提供插件坐标即可
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<version>2.5.5version>
plugin>
plugins>
build>
目标名称 | 作用 |
---|---|
sourceDirectory | 主体源程序存放目录 |
spring-boot:build-image | Package an application into a OCI image using a buildpack. |
spring-boot:build-info | Generate a build-info.properties file based on the content of the current MavenProject. |
spring-boot:help | Display help information on spring-boot-maven-plugin.Call mvn spring-boot:help -Ddetail=true -Dgoal= to display parameter details. |
spring-boot:repackage | Repackage existing JAR and WAR archives so that they can be executed from the command line using java -jar. With layout=NONE can also be used simply to package a JAR with nested dependencies (and no main class, so not executable). |
spring-boot:run | Run an application in place. |
spring-boot:start | Start a spring application. Contrary to the run goal, this does not block and allows other goals to operate on the application. This goal is typically used in integration test scenario where the application is started before a test suite and stopped after. |
spring-boot:stop | Stop an application that has been started by the ‘start’ goal. Typically invoked once a test suite has completed. |
使用 Mybatis 的逆向工程需要使用如下配置,MBG 插件的特点是需要提供插件所需的依赖:
<build>
<plugins>
<plugin>
<groupId>org.mybatis.generatorgroupId>
<artifactId>mybatis-generator-maven-pluginartifactId>
<version>1.3.0version>
<dependencies>
<dependency>
<groupId>org.mybatis.generatorgroupId>
<artifactId>mybatis-generator-coreartifactId>
<version>1.3.2version>
dependency>
<dependency>
<groupId>com.mchangegroupId>
<artifactId>c3p0artifactId>
<version>0.9.2version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.8version>
dependency>
dependencies>
plugin>
plugins>
build>
不知大家有没有发现,通常需要用到 build
标签的时候底层都会帮我们封装好,需要我们配置的地方不多。即使有些地方需要我们配置,也不会真的我们自己去写,把现成的案例复制过来就行。
所以对 build
标签来说,我们的掌握要求就是:能大致看懂就行。
Maven 官网介绍依赖机制
管理依赖最基本的办法是继承父工程,但是和 Java 类一样,Maven 也是单继承的。如果不同体系的依赖信息封装在不同 POM 中了,没办法继承多个父工程怎么办?这时就可以使用 import
依赖范围。
典型案例当然是在项目中引入 SpringBoot、SpringCloud 依赖:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>Hoxton.SR9version>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-dependenciesartifactId>
<version>2.2.6.RELEASEversion>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-dependenciesartifactId>
<version>2.3.6.RELEASEversion>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
import
依赖范围使用要求:
pom
dependencyManagement
中官网说明如下:
This scope is only supported on a dependency of type pom in the section. It indicates the dependency is to be replaced with the effective list of dependencies in the specified POM’s section. Since they are replaced, dependencies with a scope of import do not actually participate in limiting the transitivity of a dependency.
以 Windows 系统环境下开发为例,假设现在 D:\tempare\atguigu-maven-test-aaa-1.0-SNAPSHOT.jar
想要引入到我们的项目中,此时我们就可以将依赖配置为 system
范围:
<dependency>
<groupId>com.atguigu.mavengroupId>
<artifactId>atguigu-maven-test-aaaartifactId>
<version>1.0-SNAPSHOTversion>
<systemPath>D:\tempare\atguigu-maven-test-aaa-1.0-SNAPSHOT.jarsystemPath>
<scope>systemscope>
dependency>
但是很明显:这样引入依赖完全不具有可移植性,所以不要使用。如果需要引入体系外 jar
包我们后面会讲专门的办法。
专门用于编译时不需要,但是运行时需要的 jar
包。比如:编译时我们根据接口调用方法,但是实际运行时需要的是接口的实现类。典型案例是:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
可选其实就是『可有可无』。官网的解释是:
其核心含义是:Project X 依赖 Project A,A 中一部分 X 用不到的代码依赖了 B,那么对 X 来说 B 就是『可有可无』的。
在下图的例子中,对模块 pro25-module-a
来说,Maven 会采纳 1.2.12
版本。
此时 Maven 采纳哪个版本,取决于在 pro29-module-x
中,对 pro30-module-y
和 pro31-module-z
两个模块的依赖哪一个先声明。
其实 Maven 的版本仲裁机制只是在没有人为干预的情况下,自主决定 jar
包版本的一个办法。而实际上我们要使用具体的哪一个版本,还要取决于项目中的实际情况。所以在项目正常运行的情况下,jar
包版本可以由 Maven 仲裁,不必我们操心;而发生冲突时 Maven 仲裁决定的版本无法满足要求,此时就应该由程序员明确指定 jar
包版本。
其实实际开发中几乎没有什么场景需要我们开发自定义 Maven 插件,所以本节只是通过这个角度帮助我们更好的理解插件的目标和生命周期阶段之间的关系。
[略]
<packaging>maven-pluginpackaging>
下面两种方式二选一:
<dependency>
<groupId>org.apache.mavengroupId>
<artifactId>maven-plugin-apiartifactId>
<version>3.5.2version>
dependency>
<dependency>
<groupId>org.apache.maven.plugin-toolsgroupId>
<artifactId>maven-plugin-annotationsartifactId>
<version>3.5.2version>
dependency>
Mojo
类是一个 Maven 插件的核心类。
Mojo
这个单词的意思是:Maven Old Java Object,其实 mojo
这个单词本身包含魔力;符咒(袋);护身符;(人的)魅力的含义,Maven 用 Mojo
是因为它是对 POJO 开的一个小玩笑。
每一个 Mojo
都需要实现 org.apache.maven.plugin.Mojo
接口。
我们实现 Mojo
接口比较困难,幸好可以继承 AbstractMojo
,此时我们只要实现 execute()
这一个方法即可。
public class MyHelloPlugin extends AbstractMojo {
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
getLog().info("---> This is my first maven plugin. <---");
}
}
对应的 pom.xml
中的依赖: maven-plugin-api
对应 pom.xml
中的依赖:maven-plugin-annotations
// name 属性:指定目标名称
@Mojo(name = "firstBlood")
public class MyPluginOfFistBlood extends AbstractMojo {
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
getLog().info("---> first blood <---");
}
}
要在后续使用插件,就必须至少将插件安装到本地仓库。
我们需要将插件坐标中的 groupId
部分注册到 settings.xml
中
<pluginGroups>
<pluginGroup>com.atguigu.mavenpluginGroup>
pluginGroups>
Maven 根据插件的 artifactId
来识别插件前缀。例如下面两种情况:
${prefix}-maven-plugin
artifactId
:hello-maven-plugin
hello
maven-${prefix}-plugin
artifactId
:maven-good-plugin
good
这里找一个和插件无关的 Maven 工程配置才有说服力。
<build>
<plugins>
<plugin>
<groupId>com.atguigu.mavengroupId>
<artifactId>hello-maven-pluginartifactId>
<version>1.0-SNAPSHOTversion>
<executions>
<execution>
<id>helloid>
<phase>cleanphase>
<goals>
<goal>sayHellogoal>
goals>
execution>
<execution>
<id>bloodid>
<phase>validatephase>
<goals>
<goal>firstBloodgoal>
goals>
execution>
executions>
plugin>
plugins>
build>
执行已和插件目标绑定的生命周期:
这里我们可以对接 profile
这个单词中『侧面』这个含义:项目的每一个运行环境,相当于是项目整体的一个侧面。
通常情况下,我们至少有三种运行环境:
而我们这里的『环境』仍然只是一个笼统的说法,实际工作中一整套运行环境会包含很多种不同服务器:
就拿其中的 MySQL 来说,不同环境下的访问参数肯定完全不同:
开发环境 | 测试环境 | 生产环境 |
---|---|---|
dev.driver=com.mysql.jdbc.Driver dev.url=jdbc:mysql://127.0.0.1:3306/db-sys dev.username=root dev.password=123456 |
test.driver=com.mysql.jdbc.Driver test.url=jdbc:mysql://127.0.0.1:3306/test-sys test.username=dev-team test.password=123456 |
product.driver=com.mysql.jdbc.Driver product.url=jdbc:mysql://192.168.37.132:3306/prod-db-sys product.username=root product.password=123456 |
可是代码只有一套。如果在 jdbc.properties
里面来回改,那就太麻烦了,而且很容易遗漏或写错,增加调试的难度和工作量。所以最好的办法就是把适用于各种不同环境的配置信息分别准备好,部署哪个环境就激活哪个配置。
在 Maven 中,使用 profile
机制来管理不同环境下的配置信息。但是解决同类问题的类似机制在其他框架中也有,而且从模块划分的角度来说,持久化层的信息放在构建工具中配置也违反了『高内聚,低耦合』的原则。
所以 Maven 的 profile
我们了解一下即可,不必深究。
首先为每一个环境声明一个 profile
然后激活某一个 profile
其实即使我们在 pom.xml
中不配置 profile
标签,也已经用到 profile
了。为什么呢?因为根标签 project
下所有标签相当于都是在设定默认的 profile
。这样一来我们也就很容易理解下面这句话:project
标签下除了 modelVersion
和坐标标签之外,其它标签都可以配置到 profile
中。
从外部视角来看,profile
可以在下面两种配置文件中配置:
settings.xml
:全局生效。其中我们最熟悉的就是配置 JDK 1.8。pom.xml
:当前 POM 生效本地 settings.xml
文件
<profile>
<id>jdk-1.8id>
<activation>
<activeByDefault>trueactiveByDefault>
<jdk>1.8jdk>
activation>
<properties>
<maven.compiler.source>1.8maven.compiler.source>
<maven.compiler.target>1.8maven.compiler.target>
<maven.compiler.compilerVersion>1.8maven.compiler.compilerVersion>
properties>
profile>
从内部视角来看,配置 profile
有如下语法要求:
profile
天然代表众多可选配置中的一个所以由复数形式的 profiles
标签统一管理。profile
标签覆盖了 pom.xml
中的默认配置,所以 profiles
标签通常是 pom.xml
中的最后一个标签。每个 profile
都必须有一个 id
标签,指定该 profile
的唯一标识。这个 id
标签的值会在命令行调用 profile
时被用到。这个命令格式是:-D
。
一个 profile
可以覆盖项目的最终名称、项目依赖、插件配置等各个方面以影响构建行为。
build
reporting
modules
dependencies
dependencyManagement
repositories
pluginRepositories
properties
前面提到了,POM 中没有在 profile
标签里的就是默认的 profile
,当然默认被激活。
环境信息包含:JDK 版本、操作系统参数、文件、属性等各个方面。一个 profile
一旦被激活,那么它定义的所有配置都会覆盖原来 POM 中对应层次的元素。大家可以参考下面的标签结构:
<profile>
<id>devid>
<activation>
<activeByDefault>falseactiveByDefault>
<jdk>1.5jdk>
<os>
<name>Windows XPname>
<family>Windowsfamily>
<arch>x86arch>
<version>5.1.2600version>
os>
<property>
<name>mavenVersionname>
<value>2.0.5value>
property>
<file>
<exists>file2.propertiesexists>
<missing>file1.propertiesmissing>
file>
activation>
profile>
这里有个问题是:多个激活条件之间是什么关系呢?
Maven 3.2.2 之前
:遇到第一个满足的条件即可激活——或的关系。Maven 3.2.2 开始
:各条件均需满足一一且的关系。下面我们来看一个具体例子。假设有如下 profile
配置,在 JDK 版本为 1.6
时被激活:
<profiles>
<profile>
<id>JDK1.6id>
<activation>
<jdk>1.6jdk>
activation>
……
profile>
profiles>
这里需要指出的是:Maven 会自动检测当前环境安装的 JDK 版本,只要 JDK 版本是以 1.6
开头都算符合条件。下面几个例子都符合:
# 列出所有激活的 profile,以及它们在哪里定义
mvn help:active-profiles
mvn compile -P
Lambda 表达式代码要求 JDK 版本必须是 1.8
,我们可以以此来判断某个指定更低 JDK 版本的 profile
是否被激活生效。
@Test
public void test() {
new Thread(()->{
System.out.println(Thread.currentThread().getName() + " is working");
}).start();
}
以目前配置运行这个测试方法:
<profiles>
<profile>
<id>myJDKProfileid>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<version>3.1version>
<configuration>
<source>1.6source>
<target>1.6target>
<encoding>UTF-8encoding>
configuration>
plugin>
plugins>
build>
profile>
profiles>
mvn clean test -PmyJDKProfile
Maven 为了能够通过 profile
实现各不同运行环境切换,提供了一种『资源属性过滤』的机制。通过属性替换实现不同环境使用不同的参数。
<profiles>
<profile>
<id>devJDBCProfileid>
<properties>
<dev.jdbc.user>rootdev.jdbc.user>
<dev.jdbc.password>atguigudev.jdbc.password>
<dev.jdbc.url>http://localhost:3306/db_gooddev.jdbc.url>
<dev.jdbc.driver>com.mysql.jdbc.Driverdev.jdbc.driver>
properties>
<build>
<resources>
<resource>
<directory>src/main/resourcesdirectory>
<filtering>truefiltering>
resource>
resources>
build>
profile>
profiles>
dev.user=${dev.jdbc.user}
dev.password=${dev.jdbc.password}
dev.url=${dev.jdbc.url}
dev.driver=${dev.jdbc.driver}
mvn clean resources:resources -PdevJDBCProfile
我们时不时会在 resource
标签下看到 includes
和 excludes
标签。它们的作用是:
includes
:指定执行 resource
阶段时要包含到目标位置的资源excludes
:指定执行 resource
阶段时要排除的资源请看下面的例子
<build>
<resources>
<resource>
<directory>src/main/resourcesdirectory>
<filtering>truefiltering>
<includes>
<include>*.propertiesinclude>
includes>
<excludes>
<exclude>happy.propertiesexclude>
excludes>
resource>
resources>
build>
执行处理资源命令:
mvn clean resources:resources -PdevJDBCProfile
执行效果如下:
当然我们这里只是以 properties
文件为例,并不是只能处理 properties
文件。