Maven基础
1.Maven介绍
1.1 Maven是什么
概念
Maven是一个标准化的java项目管理和构建工具。
主要功能
- 提供了一套标准化的项目结构
- 提供了一套标准化的构建流程(编译、测试、打包、发布……)
- 提供了一套依赖管理机制
1.2 为什么需要Maven
Java项目通常由多个模块组成,每个模块都有许多依赖项。手动管理这些依赖项及其版本可能是非常困难和耗时的。而Maven可以自动解决这些问题,它可以帮助开发人员管理项目依赖关系,自动下载所需的库文件,并处理编译、测试和打包等操作。
使用Maven还有以下好处:
- 管理依赖关系:Maven可以自动下载并安装项目所需的所有库文件,并确保每个库文件的版本都是与其他依赖项兼容的。
- 构建项目:Maven提供了一种易于使用的方式来构建项目,开发人员只需要执行一条命令即可完成整个构建过程。
- 统一构建过程:Maven可以确保所有开发人员都遵循相同的构建流程,从而提高生产力和代码质量。
- 自动生成文档:Maven可以根据项目的注释自动生成文档,降低了文档编写的工作量。
- 支持多种IDE:Maven可以集成到多种IDE中,例如Eclipse、NetBeans和IntelliJ IDEA,方便开发人员使用。
1.2 Maven项目结构
a-maven-project
├── pom.xml ---> 项目描述文件
├── src
│ ├── main
│ │ ├── java ---> 存放Java源码
│ │ └── resources ---> 存放资源文件
│ └── test
│ ├── java ---> 存放测试源码
│ └── resources ---> 存放测试资源
└── target ---> 存放所有编译、打包、生成的文件
pom.xml
<project ...>
<modelVersion>4.0.0</modelVersion>
<groupId>com.itranswarp.learnjava</groupId>
<artifactId>hello</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<properties>
...
</properties>
<dependencies>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
</project>
groupId: 类似于Java的包名,通常是公司或组织的名称
artifactId:类似于Java的类名,通常是项目名称
一个Maven工程就是由groupId
,artifactId
和version
作为唯一标识。我们在引用其他第三方库的时候,也是通过 这三个变量确定的。例如,依赖commons-loggin
:
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
使用<dependency>
声明一个依赖之后,Maven就会自动下载这个依赖包并把它放到classpath中。
2.依赖管理
依赖管理是Maven的一个重要功能。当我们的一个项目需要依赖另一个或多个jar包时,Maven会自动下载这些依赖包,并且如果这些包又依赖于其他的一些包,Maven也会自动进行解析并且下载。这极大方便了Java项目依赖第三方包时的操作。
2.1 依赖关系
Maven定义了四种依赖关系,分别是:compile
,test
,runtime
,provided
scope | description |
---|---|
compile | 编译时需要用到该jar包(默认) |
test | 编译test时需要用到该jar包 |
runtime | 编译时不需要用到,但运行时需要用到 |
provided | 编译时需要用到,但运行时由JDK或某个服务器提供 |
// 示例
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.3.2</version>
<scope>test</scope>
</dependency>
2.2 jar包从哪里获取
Maven维护了一个中央仓库,所有的第三方库将自身的jar和相关信息上传至中央仓库,这样Maven就可以从中央仓库根据提供的依赖信息来进行下载。
但是对于同一个依赖包,Maven不会每次都从中央仓库进行下载。一个jar包一旦被下载之后,就会被Maven自动缓存在本地目录中(用户目录下的.m2
目录),下一次再使用时,只需要从本地目录导入即可。
2.3 镜像仓库
中国区用户可以使用阿里云提供的Maven镜像仓库。使用Maven镜像仓库需要一个配置,在用户主目录下进入.m2
目录,创建一个settings.xml
配置文件,内容如下:
<settings>
<mirrors>
<mirror>
<id>aliyun</id>
<name>aliyun</name>
<mirrorOf>central</mirrorOf>
<!-- 国内推荐阿里云的Maven镜像 -->
<url>https://maven.aliyun.com/repository/central</url>
</mirror>
</mirrors>
</settings>
3.构建流程
Maven不仅提供了一套标准的项目结构,同时也提供了一套标准的项目构建流程,可以自动实现编译、打包、发布等等。
Maven通过lifecycle、phase、goal来提供标准的构建流程。
3.1 lifecycle和phase
default
Maven的lifecycle(生命周期)是一套完整的操作流程,由一系列phase(阶段)构成,以内置的声明周期default为例,它包含以下phase:
- 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
当我们运行mvn package
,Maven就会执行default生命周期,它会从validate
一直运行到package
这个phase为止。
clean
clean生命周期由3个phase构成
- pre-clean
- clean (注意这个clean不是lifecycle而是phase)
- post-clean
当我们执行mvn clean package
,Maven会先执行clean生命周期并运行到clean这个phase,然后再执行default生命周期并运行到package这个phase。
在实际开发过程中,经常使用的命令有:
mvn clean
:清理所有生成的class和jar;
mvn clean compile
:先清理,再执行到compile
;
mvn clean test
:先清理,再执行到test
,因为执行test
前必须执行compile
,所以这里不必指定compile
;
mvn clean package
:先清理,再执行到package
。
大多数phase在执行过程中,因为我们通常没有在pom.xml
中配置相关的设置,所以这些phase什么事情都不做。
经常用到的phase其实只有几个:
- clean:清理
- compile:编译
- test:运行测试
- package:打包
3.2 Goal
执行一个phase又会触发一个或多个goal,goal是最小的任务单元。也就是说,执行一个生命周期在底层就是在执行多个goal。通常情况,我们总是执行phase默认绑定的goal,因此不必指定goal。
执行的Phase | 对应执行的Goal |
---|---|
compile | compiler:compile |
test | compiler:testCompile surefire:test |
goal的命名总是abc:xyz
这种形式。
3.3 插件管理
Maven在执行phase时,都会通过对应的插件来执行关联的goal。Maven本身其实并不知道如何执行phase,它只是负责找到对应的插件,然后执行默认的goal来完成编译。
例如:Maven将执行compile这个phase,这个phase会调用compiler插件执行关联的
compiler:compile
这个goal
所以,使用Maven,实际上就是配置好需要使用的插件,然后通过phase调用它们。
Maven已经内置了一些常用的标准插件:
插件名称 | 对应执行的phase |
---|---|
clean | clean |
compiler | compile |
surefire | test |
jar | package |
如何配置自定义插件
如果标准插件无法满足需求,我们还可以使用自定义插件。使用自定义插件的时候,需要声明。例如,使用maven-shade-plugin
可以创建一个可执行的jar,要使用这个插件,需要在pom.xml
中声明它:
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
...
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
自定义插件往往需要一些配置,例如,maven-shade-plugin
需要指定Java程序的入口,它的配置是:
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.itranswarp.learnjava.Main</mainClass>
</transformer>
</transformers>
</configuration>
常用的插件
- maven-shade-plugin:打包所有依赖包并生成可执行jar;
- cobertura-maven-plugin:生成单元测试覆盖率报告;
- findbugs-maven-plugin:对Java源码进行静态分析以找出潜在问题。
4.模块管理
在java软件开发过程中,对于一个大型的项目,通常会将其拆分成多个模块来进行开发,而通过Maven可以很方便的对这些模块进行管理。
例如:对于以下Maven工程来说,原来是一个大项目:
single-project
├── pom.xml
└── src
现在可以分拆成3个模块:每一个模块都可以当作一个独立的maven项目,都有各自独立的pom.xml。
mutiple-project
├── module-a
│ ├── pom.xml
│ └── src
├── module-b
│ ├── pom.xml
│ └── src
└── module-c
├── pom.xml
└── src
但是,此时module-a中的pom.xml与b、c中的pom.xml存在相当程度的重复,因此,我们可以提取出共同的部分来作为parent
,简化代码并提高复用性。
// 示例,将a,b,c中共用的pom.xml提取出来
<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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.itranswarp.learnjava</groupId>
<artifactId>parent</artifactId>
<version>1.0</version>
<packaging>pom</packaging>
<name>parent</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.28</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.5.2</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
注意到parent的<packaging>
是pom
而不是jar
,因为parent
本身不含任何Java代码。编写parent
的pom.xml
只是为了在各个模块中减少重复的配置。现在我们的整个工程结构如下:
multiple-project
├── pom.xml
├── parent
│ └── pom.xml
├── module-a
│ ├── pom.xml
│ └── src
├── module-b
│ ├── pom.xml
│ └── src
└── module-c
├── pom.xml
└── src
此时,模块A、模块B、模块C都可以直接从parent
继承,大幅简化了pom.xml
的编写。
如果模块A依赖模块B,则模块A需要模块B的jar包才能正常编译,我们需要在模块A中引入模块B:
...
<dependencies>
<dependency>
<groupId>com.itranswarp.learnjava</groupId>
<artifactId>module-b</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
最后,在编译的时候,需要在根目录创建一个pom.xml
统一编译:
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.itranswarp.learnjava</groupId>
<artifactId>build</artifactId>
<version>1.0</version>
<packaging>pom</packaging>
<name>build</name>
<modules>
<module>parent</module>
<module>module-a</module>
<module>module-b</module>
<module>module-c</module>
</modules>
</project>
这样,在根目录执行mvn clean package
时,Maven根据根目录的pom.xml
找到包括parent
在内的共4个<module>
,一次性全部编译。
5.mvnw
我们使用Maven时,基本上只会用到mvn
这一个命令。有些童鞋可能听说过mvnw
,这个是啥?
mvnw
是Maven Wrapper的缩写。因为我们安装Maven时,默认情况下,系统所有项目都会使用全局安装的这个Maven版本。但是,对于某些项目来说,它可能必须使用某个特定的Maven版本,这个时候,就可以使用Maven Wrapper,它可以负责给这个特定的项目安装指定版本的Maven,而其他项目不受影响。
简单地说,Maven Wrapper就是给一个项目提供一个独立的,指定版本的Maven给它使用。
安装与使用
安装Maven Wrapper最简单的方式是在项目的根目录(即pom.xml
所在的目录)下运行安装命令:
mvn -N io.takari:maven:0.7.6:wrapper
它会自动使用最新版本的Maven。注意0.7.6
是Maven Wrapper的版本。最新的Maven Wrapper版本可以去官方网站查看。
如果要指定使用的Maven版本,使用下面的安装命令指定版本,例如3.3.3
:
mvn -N io.takari:maven:0.7.6:wrapper -Dmaven=3.3.3
安装后,查看项目结构:
my-project
├── .mvn
│ └── wrapper
│ ├── MavenWrapperDownloader.java
│ ├── maven-wrapper.jar
│ └── maven-wrapper.properties
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
├── main
│ ├── java
│ └── resources
└── test
├── java
└── resources
发现多了mvnw
、mvnw.cmd
和.mvn
目录,我们只需要把mvn
命令改成mvnw
就可以使用跟项目关联的Maven。例如:
mvnw clean package
在Linux或macOS下运行时需要加上./
:
./mvnw clean package
Maven Wrapper的另一个作用是把项目的mvnw
、mvnw.cmd
和.mvn
提交到版本库中,可以使所有开发人员使用统一的Maven版本。