参考文献
永远最好的官网
超赞maven系列文章
1. 为啥要有maven
想当初,刚毕业刚工作,之前学的C++,java不懂,部门用的Spring,于是开始学习SSM,妈的,jar包好难整,还要一个个下载好放到libs目录中。
后来遇到好几次jar包冲突,还好有maven的idea插件,才让问题快速找到原因。
因此maven可以解决
- jar包的下载难
- jar包版本之间的依赖问题
- jar包版本冲突【maven并没有很好的解决,工作还是经常遇到jar包冲突的问题,这个时候你需要一个排查思路】
- 项目的目录结构不同
- 项目的生命周期控制方式五花八门
官方解释什么是maven:maven是apache软件基金会组织维护的一款自动化构建工具,专注服务于java平台的项目构建和依赖管理。
2. maven的安装
3. maven的配置文件,重点之中【待完善】
这块其实水很深,不同的maven版本和配置都有可能导致最终打出来的包不一样。
曾经我们就遇到配置的不同导致打出来的包不同,最后导致线上问题,真的是。。。。。太难了。。。
4. maven约定的项目配置
4.1 通过idea查看项目依赖
4.2 idea maven helper插件
查包依赖,排包的好工具!!!
4.3 pom导入jar包格式
<dependencies><!-- 在这里添加你的依赖 --><dependency><groupId></groupId><artifactId></artifactId><version></version><!--依赖的类型,表示所要依赖的构件的类型,对应于被依赖的构件的packaging。大部分情况下,该元素不被声明,默认值为jar,表示被依赖的构件是一个jar包。还有pom等--><type></type><!--依赖的范围 重点!!!--><scope></scope><!--标记依赖是否可选--><optional></optional><!--用来排除传递性的依赖 排包重点!!!--><exclusions><exclusion></exclusion><exclusion></exclusion></exclusions></dependency></dependencies>
4.4 重点的scope
控制jar包是否打包到项目的classpath中。
还有一个import
有的时候我们会从在项目中加第三方的jar包,因为都是私有的项目,他们只给了一个jar包,maven仓库中没有,那这个时候怎么办呢?
4.5 用mvn deploy到自己的私有仓库中
mvn install:install-file -Dfile=D:\*.*-1.0.jar -DgroupId=*.* -DartifactId=*-* -Dversion=1.0 -Dpackaging=jar
4.6 使用system
似乎这里和上述文章说的不一致,那我们来搞个测试。
发现打完包之后的确没有这个jar包。需要注意的是:使用第二种方式导入的jar包,因为scope指定的是system类型,因此打包时并不会被打入到最终的jar中;如果需要和项目一起打包则需要使用springboot的打包插件。
<plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><!--设置为true,以便把本地的system的jar也包括进来--><includeSystemScope>true</includeSystemScope></configuration>
</plugin>
4.7 maven解析pom中依赖调解的原则
- 路径最近原则
上面A->B->C->Y(1.0),A->D->Y(2.0),Y的2.0版本距离A更近一些,所以maven会选择2.0。
但是如果出现了路径是一样的,如:A->B->Y(1.0),A->D->Y(2.0),此时maven又如何选择呢?
- 最先声明原则
如果出现了路径一样的,此时会看A的pom.xml中所依赖的B、D在dependencies中的位置,谁的声明在最前面,就以谁的为主,比如A->B在前面,那么最后Y会选择1.0版本。
这两个原则就是解决jar包冲突的理论依据,必须理解!!!!:路径最近原则、最先声明原则。
4.8 optional选项作用
官网介绍
排包还有一个思路
A->B中scope:compile
B->C中scope:compile
如果我们不想引入C
那么就可以在B项目中引入Cjar包的时候把optional设置为true。
5. maven仓库
一句话:maven引包只是一个引用,在最终打包后才会在包中。
5.1 仓库的分类
- 本地仓库
maven本地仓库默认地址是~/.m2/respository
目录,这个默认我们也可以在~/.m2/settings.xml
文件中进行修改:
<localRepository>本地仓库地址</localRepository>
- 私有仓库
自己公司的maven私有仓库,有可能工作的时候这个仓库就需要你维护或者搭建。
maven的私有仓库现在也有两个不同的版本了: Nexus 2/3。文件存储的格式好像又比较大的改动。。。。
总体上来说私服有以下好处:
加速maven构件的下载速度
节省宽带
方便部署自己的构件以供他人使用
提高maven的稳定性,中央仓库需要本机能够访问外网,而如果采用私服的方式,只需要本机可以访问内网私服就可以了
- 中央仓库
不需要我们配置,他是写好在maven中,如下地方
apache-maven-3.6.1\lib\maven-model-builder-3.6.1.jar\org\apache\maven\model\pom-4.0.0.xml
仓库地址:
https://repo.maven.apache.org/maven2
访问一下
中仓仓库查找jar包的网站:https://search.maven.org/
新的中仓仓库查找jar包的网站:https://central.sonatype.com/
- 其他公共远程仓库
比如像阿里、网易等等
5.2 重点!!快照版本和正式版本
version以-SNAPSHOT结尾的,表示这是一个不稳定的版本,这个版本我们最好只在公司内部测试的时候使用,最终发布的时候,我们需要将-SNAPSHOT去掉,然后发布一个稳定的版本,表示这个版本是稳定的,可以直接使用,这种稳定的版本我们叫做release版本。
- 开发的时候使用快照版本,这个时候每次打包都会去拉去最新的版本,如果你改动的快照包的代码,就需要deploy后让引用的也无妨重新打包部署一下。。。
- 正式版本只要有了就不会再去私有仓库中拉取了。注意:有的时候本地搞出来了一个错误的正式包,但是因为是正式包,本地有了就不会去仓库中拉,所以这个时候需要手动删除本地的错误的包
5.3 Maven中远程仓库的配置
5.3.1. 使用项目的pom文件配置
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.javacode2018</groupId><artifactId>maven-chat03</artifactId><version>1.0-SNAPSHOT</version><dependencies><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.62</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>2.2.1.RELEASE</version></dependency></dependencies><repositories><repository><id>aliyun-releases</id><url>https://maven.aliyun.com/repository/public</url><releases><enabled>true</enabled></releases><snapshots><enabled>false</enabled></snapshots></repository></repositories></project>
repository元素说明:
id:远程仓库的一个标识,中央仓库的id是central,所以添加远程仓库的时候,id不要和中央仓库的id重复,会把中央仓库的覆盖掉
url:远程仓库地址
releases:主要用来配置是否需要从这个远程仓库下载稳定版本构建
snapshots:主要用来配置是否需要从这个远程仓库下载快照版本构建
enabled属性,是个boolean值,默认为true,表示是否需要从这个远程仓库中下载稳定版本或者快照版本
5.3.2. settings.xml配置
太难了,搞不懂。。。。。
私有仓库搭建
最常用的是Nexus
6. maven插件
6.1 介绍
6.2 生命周期和阶段
每个生命周期中的后面的阶段会依赖于前面的阶段,当执行某个阶段的时候,会先执行其前面的阶段。
6.3 mvn命令
如何执行mvn的命名
mvn 阶段1 [阶段2] [阶段n]
6.4 插件的目标和参数查看
- 列出插件所有目标 ,使用插件的help目标查看或者用help插件
mvn 插件goupId:插件artifactId[:插件version]:help
mvn 插件前缀:helpmvn help:describe -Dplugin=插件goupId:插件artifactId[:插件version]
mvn help:describe -Dplugin=插件前缀
- 查看插件目标参数列表,使用
-Dgoal=目标名称 -Ddetail
参数查看目标参数或者用help插件
mvn 插件goupId:插件artifactId[:插件version]:help -Dgoal=目标名称 -Ddetail
mvn 插件前缀:help -Dgoal=目标名称 -Ddetailmvn help:describe -Dplugin=插件goupId:插件artifactId[:插件version] -Dgoal=目标名称 -Ddetail
mvn help:describe -Dplugin=插件前缀 -Dgoal=目标名称 -Ddetail
- 命令行运行插件
mvn 插件goupId:插件artifactId[:插件version]:插件目标 [-D目标参数1] [-D目标参数2] [-D目标参数n]
mvn 插件前缀:插件目标 [-D目标参数1] [-D目标参数2] [-D目标参数n]
- 查看插件的前缀
mvn help:describe -Dplugin=插件goupId:插件artifactId[:插件version]
6.5 插件的目标和生命周期阶段绑定
<build><plugins><plugin><!-- 插件 --><groupId>org.apache.maven.plugins</groupId><artifactId>maven-source-plugin</artifactId><version>3.2.0</version><executions><!-- 插件需要执行的任务 --><execution><!-- 任务的id,需唯一,如果不指定,默认为default --><id>attach-source</id><!-- 任务中插件的目标,可以指定多个 --><goals><goal>jar-no-fork</goal></goals><!-- 绑定的阶段 --><phase>verify</phase></execution></executions></plugin></plugins></build>
查看插件的默认绑定,和查看参数一样
mvn help:describe -Dplugin=插件goupId:插件artifactId[:插件version] -Dgoal=目标名称 -Ddetail
mvn help:describe -Dplugin=插件前缀 -Dgoal=目标名称 -Ddetail
~/work/ mvn help:describe -Dplugin=source -Dgoal=jar-no-fork -Ddetail
[INFO] Mojo: 'source:jar-no-fork'
source:jar-no-forkDescription: This goal bundles all the sources into a jar archive. Thisgoal functions the same as the jar goal but does not fork the build and issuitable for attaching to the build lifecycle.Implementation: org.apache.maven.plugins.source.SourceJarNoForkMojoLanguage: javaBound to phase: packageAvailable parameters:
Bound to phase: package
6.6 插件传参的方式
6.6.1. mvn命令-D属性名称的方式传递
mvn 插件goupId:插件artifactId[:插件version]:插件目标 [-D目标参数1] [-D目标参数2] [-D目标参数n]
6.6.2. pom.xml中properties中定义的方式指定。
<properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</java.version><maven.test.skip>true</maven.test.skip>
</properties>
6.6.3. pom的plugin里配置插件的参数
6.6.3.1 插件目标共享参数配置
<build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><version>2.12.4</version><!-- 插件参数配置,对插件中所有的目标起效 --><configuration><skip>true</skip></configuration></plugin></plugins>
</build>
6.6.3.2 插件目标参数配置
<build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><version>2.12.4</version><executions><execution><goals><goal>test</goal><goal>help</goal></goals><phase>pre-clean</phase><!-- 这个地方配置只对当前任务有效 --><configuration><skip>true</skip></configuration></execution></executions></plugin></plugins>
</build>
6.7.插件仓库配置
<pluginRepositories><pluginRepository><id>myplugin-repository</id><url>http://repo1.maven.org/maven2/</url><releases><enabled>true</enabled></releases></pluginRepository>
</pluginRepositories>
7 maven的聚合、继承、单继承问题
7.1 聚合也就是多模块
这里没啥好说的,实际看过就知道了,但是重点讲一下
<modules><module>模块1</module><module>模块2</module><module>模块n</module>
</modules>
<package>pom</package>
注意上面的module元素,这部分是被聚合的模块pom.xml所在目录的相对路径。
package的值必须为pom。
7.2 多模块中子构件的pom.xml引入父构件的配置:
<parent><groupId>父构件groupId</groupId><artifactId>父构件artifactId</artifactId><version>父构件的版本号</version><relativePath>父构件pom.xml路径</relativePath>
</parent>
relativePath表示父构件pom.xml相对路径,默认是…/pom.xml,所以一般情况下父子结构的maven构件在目录结构上一般也采用父子关系。单有的时候是评级的,这个时候就需要relativePath正确的配置了。
mvn dependency:tree 这个插件可以根据pom.xml的配置,列出构件的依赖树信息。
也可以用来排包
7.3 依赖管理(dependencyManagement)
maven提供的dependencyManagement元素既能让子模块继承到父模块的依赖配置,又能保证子模块依赖使用的灵活性,在dependencyManagement元素下声明的依赖不会引入实际的依赖,他只是声明了这些依赖,不过它可以对dependencies中使用的依赖起到一些约束作用。
父项目写好dependencyManagement做好版本管理,子项目引用的时候不需要写版本。
7.4 单继承的问题和解决
如上,dependencyManagement可以让子项目配置更简单。
但是实际开发中我们自己的项目就是多模块的,此时又想引入其他项目的dependencyManagement怎么办呢?
7.4.1 回到scope中没有说的import
可以在我们的项目中使用加入下面配置:
<dependencyManagement><dependencies><dependency><groupId>com.javacode2018</groupId><artifactId>javacode2018-parent</artifactId><version>1.0-SNAPSHOT</version><type>pom</type><scope>import</scope></dependency><dependency>构件2</dependency><dependency>构件3</dependency><dependency>构件n</dependency></dependencies>
</dependencyManagement>
<scope>import</scope>
就可以做到这个效果,相当于把导入的配置像C语言的宏定义一样使用。
7.5 插件管理(pluginManagement)
和依赖管理(dependencyManagement)管理一样,父项目配置好插件,子项目也可以不配置版本了。
父项目
<build><pluginManagement><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-source-plugin</artifactId><version>3.2.0</version><executions><execution><id>attach-source</id><phase>verify</phase><goals><goal>jar-no-fork</goal></goals></execution></executions></plugin></plugins></pluginManagement>
</build>
子项目
<build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-source-plugin</artifactId></plugin></plugins>
</build>
聚合主要是为了方便多模块快速构建。
而继承主要是为了重用相同的配置。
对于聚合来说,聚合模块是知道被聚合模块的存在的,而被聚合模块是感知不到聚合模块的存在。
对于继承来说,父构件是感知不到子构件的存在,而子构件需要使用parent来引用父构件。
两者的共同点是,聚合模块和继承中的父模块的package属性都必须是pom类型的,同时,聚合模块和父模块中的除了pom.xml,一般都是没有什么内容的。
实际使用是,我们经常将聚合和继承一起使用,能同时使用到两者的优点。
8. 大神写的太好一定要看-mvn命令1
9. 大神写的太好一定要看-maven命令2
10. 排包
生个版本,嘿,项目启动报错。或者是启动不报错,运行报错。。。。如:
没有这个类 NoSuch
没有这个方法 NoSuch
如何处理呢:
- 通过报错信息查找到
类->包
,注意有的时候可能是类的静态初始化抛异常了
- 通过maven helper插件找到冲突的包
- 排包:A: 使用exclusions;B: 使用maven的依赖解析原则,直接在项目的pom文件中配置好版本,这样让maven首先就找到正确的版本。