Maven
是一个项目构建,管理的工具。现代Java
项目大部分都使用,但也有一些项目使用Gradle
,Maven
仍然是主流,学习使用maven
十分有必要
Maven下载
打开apache maven
的Maven下载地址
找到Download
的Files
模块,Mac/Linux
系统下载.tar.gz
后缀的压缩包,windows
系统下载.zip
后缀的压缩
下载后解压到目标目录即可
开始
- 解压后,新建环境变量名为
M2_HOME
,指向maven
的解压目录(类似于配置JAVA_HOME
),unix
环境可以用tar -zxvf xx.tar.gz
进行解压 - 添加环境变量使终端可访问
mvn
,unix
系统修改shell
相关配置文件中,添加两条命令
M2_HOME=[将tar.gz解压后的目录]
PATH=$PATH:$M2_HOME/bin
export PATH
windows
系统只需要通过GUI
添加一条PATH
环境变量即可:%M2_HOME%\bin
- 环境变量添加后,在命令行工具中即可使用
mvn
命令 - 输入
mvn helper:system
会到远程仓库中下载一些默认的jar
包,成功后会输出一系列log
修改本地仓库目录和镜像源
默认情况下,maven
会在当前用户目录下创建一个~/.m2
文件夹,默认情况下会将所有相关依赖jar
都下载刀此处,如果不想(尤其是windows
用户C
盘紧张),可以更改为其他目录。
打开maven
目录下的config/setting.xml
,添加几处配置
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.2.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.2.0 https://maven.apache.org/xsd/settings-1.2.0.xsd">
<localRepository>~/xxxx/你的目标存放目录</localRepository>
<pluginGroups>
</pluginGroups>
<proxies>
</proxies>
<servers>
</servers>
<mirrors>
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>
</mirrors>
</settings>
-
默认文件中,会有很多注释,这里我去掉精简了下,主要看
localhostRepository
标签,通过这个选项修改默认的仓库地址,目录不存在时会自动创建 -
配置镜像地址,国内访问
maven
中心仓库的速度不见得乐观,可以改为阿里云的镜像地址,添加了mirror
标签,并设置了国内镜像地址
如果不想在根配置文件配置源,也可以在某个项目的pom.xml
(后面会说到)进行单独配置
<repositories>
<repository>
<id>maven-ali</id>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
<checksumPolicy>fail</checksumPolicy>
</snapshots>
</repository>
</repositories>
仓库的分类
当maven
下载依赖时,会从远程中央仓库中(需要网络)下载,但是如果下载一次后,便会存在本地缓存仓库,后续解析依赖时,如果检索到本地仓库存在,便不会再去远程仓库中下载
所以可以分为两类
- 本地仓库,开发电脑上的文件夹,里面存放着下载好的
jar
包 - 远程仓库,这里又分为几种情况,因为都需要网络进行操作,所以都可以归类为远程仓库
- 中央仓库,
maven
核心仓库 - 私服/镜像仓库,类似于
aliyun
这种就属于镜像仓库,中央仓库有的基本都有。私服:可能基于某种安全考虑,会在内网搭建一个内网私服,只能在当前的局域网内使用,外网无法访问
- 中央仓库,
配置maven
maven
很强大的一点在于,通过约定大于配置的规则,让很多原本需要手动管理的模块依赖(第三方库)变得不再繁琐,如A.jar
中依赖了B.jar
,如果不引入B.jar
则无法使用A.jar
的类,他们之间的关系就叫做依赖,而maven
可以通过简单的配置进行项目依赖管理。
maven
项目有它专属的约定项目结构,如下图(和idea
开发工具没关系,这里只是用它打开项目,不会用到任何功能,项目目录结构符合即可)
pom
是Project Object Model
的缩写,项目对象模型,maven
把一个项目当做一个模型使用
<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.mgl</groupId>
<artifactId>testMvn</artifactId>
<version>1.0.0</version>
<properties>
<java.version>8</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
</properties>
</project>
-
modelVersion
对于maven2/3
版本只能写4.0.0
,指定遵循哪个版本的项目描述符 -
groupId
为组织id
,一般用域名倒写 -
artifactId
为项目模块名称 -
version
为版本号,groupId+artifactId+version
组成了唯一标识,通常称三个的组合为坐标gav
关于properties
的配置项
project.build.sourceEncoding
指定了编译时maven
读取文件使用的编码,不设置的话默认时平台的编码,可以用mvn -v
查看java.version
定义了一个变量方便使用,其他地方通过${标签名}
引用maven.compiler.source
是配置java
源代码使用的最高语言特性版本,如源代码使用了java8
中的特性:lambda
表达式,source
却指定了java7
,那么编译必定不通过,反之则可以maven.compiler.target
配置编译指定class
字节码文件最低可运行的JVM
版本,如指定了java8
,则class
文件会含有相关版本信息,当跑在虚拟机时如果不兼容,则会发生问题
实际上就是指定了javac
工具的选项(maven
也是java
编写的),一般指定为当前JDK
版本即可
如此,当前项目就是一个比较标准且精简的maven
项目
Maven常用的生命周期,命令,插件
有了项目结构后,还要知道如何使用maven
mvn clean
此命令是用于清理编译后的产出目录(compile
命令的编译输出文件)mvn compile
会将所有src/main/java
目录下的java
文件编译成class
字节码文件,并且输出到当前工作目录的target
目录中(也就是clean
命令会删除的目录)mvn test-compile
会将所有src/test/java
目录下的java
文件编译成class
字节码文件- 这里值得一提的是,会按照一个固定周期执行,如果执行了
test-compile
那么,compile
也会被执行,并且先于test-compile
后面的以此类推。以一个例子来类比:把大象塞进冰箱分为四步,第一步举起大象,第二步打开冰箱门,第三步把大象塞进冰箱,第四步关上冰箱门。compile
就是打开冰箱门这一步,而test-compile
就是把大象塞进冰箱,执行这一步时必定要先打开冰箱门(也就是compile
) mvn test
会执行src/test/main
中的测试程序,一般用于单元测试mvn package
打包动作,默认配置为jar
,可以通过pom.xml
进行修改,执行后会将当前项目的src/main/java
的代码,依赖(决定于依赖的scope
,后面会说)和资源进行编译打包成jar
,对于test
目录的内容不关心mvn install
将jar
包安装到本地的maven
仓库,也就是上面修改过的本地缓存目录,后续在pom.xml
中便能利用坐标进行引入依赖
mavn compile
首先在main/java/cn/mgl/Cat.java
文件中,随意编写点代码
接着在当前工作目录执行mvn compile
将会看到以上的日志输出,其中maven
有一些生命周期的默认插件,在执行mvn help:system
时就已经安装好了,如果没有那么执行时也会将缺少的依赖进行下载
maven-resources-plugin:2.6
插件会将src/main/resources
目录中所有文件复制到输出目录下maven-compiler-plugin:3.1
会将src/main/java
目录中所有java
文件编译成字节码文件
观察项目目录
可以看到有一个
target/classes
文件夹,这里放的就是目前项目中被编译后的class
文件,为了演示效果,特地建了一个空的application.yml
,默认规则会将资源文件原封不动的复制到编译目录当中
在平时用maven
架构开发项目时, classpath:
一般指的就是target/classes
目录
mvn clean
这个命令比较简单,执行后就会将target
文件夹整个删除
mvn test-compile
作用与mvn-compile
类似,区别只是作用的目录不一样,比较简单不演示
mvn test(延伸出安装依赖)
这个命令作用在于运行单元测试代码,所谓的单元测试简单的理解为就是对项目中每个java
编写的方法进行运行测试检验是否符合预期,这个行为可以通过一些主流测试框架来完成,如junit
回到pom.xml
中,使用dependencies
标签进行依赖声明
<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
省略...
<properties>
省略...
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
dependencies
是关键,它用于声明当前项目中有哪些依赖项,在此处配置后,即可通过maven
将所有junit
依赖关系中的依赖包(包括其自身)下载到本地中使用
子标签dependency
可以有多个,其中的子标签写法其实就是上述讲过的坐标,所有类库的依赖信息都可以在中央仓库中寻找,地址为mvnrepository.com/,如想引入mybatis
在网站中搜索mybatis
,找到对应的maven
坐标复制到当前项目中的pom.xml
需要关注一下其中的scope
标签,这个标签的作用置顶依赖的作用范围,也就是在maven
构建的哪些阶段生效(编译,测试,打包,安装,部署)
compile
不写时默认就是这个值,作用是在每一个阶段都需要该依赖test
只有在test
阶段才需要的依赖,其他阶段不会被编译等行为provider
只有编译阶段依赖有效。比如编写一个web
项目,最终会打包成一个war
包丢给tomcat
服务器,那么服务器本身有servlet
等jar
包,只需要保证编译不报错,这种情况适合使用这个scope
接着在src/test/java
目录中的java
文件编写如下代码
package cn.mgl;
import org.junit.Assert;
import org.junit.Test;
public class TestCat {
@Test
public void test001() {
int i = 100;
Assert.assertEquals(100, i);
}
}
接着运行mvn test
可以看到maven-surefire-plugin:2.12.4
的作用就是帮助项目执行测试代码,并且将编写的测试程序执行后输出测试结果报告
mvn package
没有在pom.xml
中指定packing
标签的值时,默认就是jar
,所以执行后,会将src/main
编译后的产物打包成一个jar
包,其实就是一个压缩包,将后缀名改成rar
就可以用解压软件解压
其中对应的插件为maven-jar-plugin:2.4
,在target
目录下会有一个产出文件,命名规则是根据项目的artifactId
+version
生成的
mvn install
执行命令后,会将打包后的jar
包安装的本地依赖目录当中
目录是根据groupId
进行生成的,这个试例用的groupId
是cn.mgl
那么就会生成cn/mgl
目录,在本地的项目中,想用这个jar
包时只需要在pom.xml
添加坐标即可使用
到这里,基本的maven
命令都有了个初步认识,上面所有列出的插件都是可以进行配置化的,以及可以自己添加一些额外的插件供maven
的特定阶段去使用。如一开始编写pom.xml
中,有一个配置source
和taget
的配置项,可以通过在对应的compiler
中进行配置,如下方示例
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
</build>
像其他的默认插件如果没有定制化需求,可以不编写
如何将src/main中的非java文件也编译到target
当中
在有些情况下,可能确实是需要这种处理,如使用mybatis
框架时,有一种mapper.xml
和mapper[interface]
需要放到同一个目录下的要求,此时就必须通过resources
标签才进行资源的配置化
为了演示效果,特地添加了一个
aa.js
的空文件到cn.mgl
包下,此时执行mvn compile
后,target
目录下并不会有这个文件存在
在pom.xml
添加配置
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.js</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>*</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
这里添加了两个resource
标签,原因是如果手动配置后,默认的规则会失效,那么resources
里的文件就不会被替换,所以需要将其补到规则当中
directory
配置资源的目录<include>**/*.js</include>
的作用是在directory
目录下的任意结构下的js
文件都复制到编译后到产出目录当中
执行
mvn compile
后,资源就都复制成功了
通过IDEA使用Maven
上面的示例中仅仅是用idea
打开了项目,但maven
命令都是通过命令行输入执行的,目的是想描述maven
和idea
无强关联关系,使用其他工具或者不使用工具都是可以使用maven
构建项目的。
如果用idea
创建的maven
项目,有GUI
供方便使用(idea
新旧版都一样)
Lifecycle
表示生命周期,双击某一项会执行对应的命令,比如双击途中的Lifecycle:clean
,实际上等价于在当前工作目录执行mvn clean
Plugin
表示maven
构建过程用到的插件依赖Dependencies
表示当前项目中所有依赖项及传递性依赖
点击黄框中的按钮都可以进行加载依赖项
maven的父级依赖
pom.xml
中有一个可以标签的标签为parent
,它的作用类似于Java
中的继承,方便配置的复用,常用的框架Springboot
中就有这种使用,在使用一些内置依赖时不需要写版本后,maven
也能找到对应的目标,原因就是父pom
中有相关配置
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.5</version>
<relativePath/>
</parent>
这是一个基于SpringBoot
搭建的应用项目,pom
文件的开头就写了一个parent
标签,说明项目是一个父子结构模式
relativePath
是指定去哪个地方找这一份需要继承的pom
描述,当指为空(空标签)时,会去仓库找对应的pom
。当不写的时候,默认就是../pom.xml
,像多模块的项目,最外层定义一个pom.xml
为父pom
,与这个文件同级别创建子模块,子模块又有各自的pom
并且继承了父pom
,来达到可复用,可管理的作用
在idea
中按住ctrl
点击artifactId
可以跳转到父pom
(自己去仓库找也行)
<!-- 可以看到父pom又继承了一个dependencies -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.5.5</version>
</parent>
此时会发现父pom
也有父pom
,再递进查看爷pom
文件内容
<properties>
<activemq.version>5.16.3</activemq.version>
<antlr2.version>2.7.7</antlr2.version>
<appengine-sdk.version>1.9.91</appengine-sdk.version>
<artemis.version>2.17.0</artemis.version>
<aspectj.version>1.9.7</aspectj.version>
<assertj.version>3.19.0</assertj.version>
<atomikos.version>4.0.6</atomikos.version>
<awaitility.version>4.0.3</awaitility.version>
<!-- 省略 -->
<properties>
主要看这个文件的核心定义,这里爷爷pom
定义了一堆的properties
,对一些仓用的依赖进行了版本定义
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-amqp</artifactId>
<version>${activemq.version}</version>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-blueprint</artifactId>
<version>${activemq.version}</version>
</dependency>
<!-- 省略 -->
</dependencyManagement>
dependencyManagement
标签,顾名思义就是用来管理依赖的,在这里定义的依赖默认不会被下载,只有当真正的子pom
中引入时,才会下载对应包(某种意义的按需引入)
Springboot
的用法就是一个很好的实践,在有需要进行多模块依赖管理时,可以参考这种设计
maven解决依赖冲突规则
在某些情况下,一些不同的依赖他们自身或传递依赖引入了不同版本的同一个包。例如项目引入了a和b
依赖,a
依赖引入了d
包的1.0
版本,而b
依赖引入了d
包的2.0
,此时就会发生所谓的依赖冲突问题
而maven
有它自己的解决机制,首先会去检测当前pom
的传递性依赖树,基准是不允许出现同名但不同版本的jar
包,否则就会根据特定规则去解决保证只留存一个
-
当相同依赖(忽略版本)在依赖树中为同级别时,在
pom.xml
声明越前的优先级越高 -
当相同依赖(忽略版本)在依赖树中不同级别时,距离根节点越近的优先级越高
这个图是通过idea
的一个插件【maven dependency helper
】提供,当前项目中含有mybatis-spring-boot-starter
两个同名但不同版本的依赖包,跟据规则,层级较浅的优先级高一点,所以maven
会抛弃调1.3.2
的jar
包,采用2.1.3
的
不用插件的话,通过idea
的maven
工具栏也可以看出低版本的依赖被置灰了,冲突中胜出的会高亮
以及在pom
文件右键选择Diagrams->Show Dependencies...
也可以查看依赖结构树
上面说的是maven
的规则,有有时候这种规则最终存留的jar
包版本可能并不是预期想要的,x.jar
在依赖树中有1.0,2.0,3.0
三个版本,若按照maven
解决冲突的机制,存在下来的可能是1.0
或者2.0
,实际需要的是3.0
,此时有以下几种方式可以解决
- 在
pom.xml
中声明目标依赖版本,此处优先级最高,按照maven
规则会淘汰掉其他传递性依赖中的版本 - 在确定某个插件不需要其传递性依赖时,通过
exclusions
标签配置需要排除的jar
包信息,已上面演示的springboot
项目为例,在引入分页插件依赖时,手动的将mybatis-spring-boot-starter
依赖去掉
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.5</version>
<exclusions>
<exclusion>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
再次查看依赖树,分页插件当中已经没有mybatis-spring-boot-starter:1.3.2
的相关依赖了
可以通过命令mvn dependency:tree
查看项目当前的依赖树,不过输出的内容是经过maven
解决了依赖冲突之后的树结构
使用maven
第三方插件
Flyway
是一个数据库的版本插件
官方使用地址:flywaydb.org/documentati…
- 首先在项目
pom
中添加一个build
下的plugin
<plugins>
<plugin>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-maven-plugin</artifactId>
<version>5.2.4</version>
<configuration>
<url>jdbc:mysql://localhost:3306/【你的数据库名称】</url>
<user>【你的用户名】</user>
<password>【你的密码】</password>
</configuration>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
</dependencies>
</plugin>
</plugins>
在添加插件时,可以给插件添加一些依赖,在plugin
标签里用dependency
,配置一些插件属性可以通过configuration
标签,具体可以配置哪些需要查看插件的文档,这里设置一些链接信息,导入maven
依赖,在idea
的插件视图中应该能看到和flyway
相关的信息
使用方式有两种,要么借助idea
的视图双击某一个节点,即可执行对应命令,第二种使用命令行方式,比如想执行migrate
,那么在当前工作目录输入mvn flyway:migrate
使用flyway
需要在resources
目录下创建db/migration
,这个目录放固定格式的sql
文件,插件执行时会扫描这个资源目录,创建一个sql
文件,文件名格式有要求:V1__Create__User.sql
,要以V[版本号]
开头,后面的描述间隔都是以双下划线分割
执行插件动作前请确定数据库可以访问,执行mvn flyway:migrate
查看数据库即可看到表已经创建成功
maven的内容还有很多,想要更深入了解都可以在官网文档或者第三方maven
插件中寻找相关使用说明
完:)