仓库
本地仓库:
maven项目实际目录下,是不存放依赖文件的,当编译或者测试需要用到依赖文件时,maven基于仓库坐标会去对应的仓库查找依赖项,首先在本地仓库中找,然后下载到本地仓库再使用,若本地仓库与远程仓库均没有,报错。构件只有在本地仓库,才能被maven项目使用,构件如何进入本地仓库:远程仓库下载,本地项目mvn install。
远程仓库:
本地仓库只有一个,远程仓库可以有多个:中央仓库、私服、其他公共库
中央仓库:是maven自带的默认远程仓库,里面包含了绝大多数流行开源Java构件,这就是maven可以开箱即用的原因。
私服:架设在局域网的远程仓库,maven需要下载构件时,请求私服,私服有就返回,没有,则从外部远程仓库下载,缓存到私服,然后返回给maven用户。
其他公共库:如java.net maven库
maven的一些约定在超级pom中,超级pom的位置是:/lib/maven-model-builder-xxx.jar中的org/apache/maven/model/pom-4.0.0.xml,超级pom是任何项目都会继承的pom,里面包含了中央仓库地址,插件仓库地址,项目的一些约定等。
代码块
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<!--组织标识,定义了项目属于哪个组-->
<groupId>com.xuexi</groupId>
<!-- 项目名称,定义当面Maven项目在组中唯一的ID-->
<artifactId>honour</artifactId>
<!-- 项目当前的版本号-->
<version>1.0.0-SNAPSHOT</version>
<!-- POM模型版本-->
<modelVersion>4.0.0</modelVersion>
<!-- 打包的格式,可以为:pom , jar , maven-plugin , ejb , war , ear , rar , par -->
<packaging>war</packaging>
<properties>
<!-- 为pom定义一些常量,在pom中的其它地方可以直接引用-->
</properties>
<dependencyManagement>
<!-- 对所依赖jar包进行版本管理的管理器-->
</dependencyManagement>
<dependencies>
<!-- 对所依赖jar包进行版本管理的管理器-->
</dependencies>
<!--工程源码目录结构&输出目录--!>
<build>
<plugins>
给出构建过程中所用到的插件。
<!-- -->
</build>
<profiles>
<!-- -->
</profiles>
</project>
dependencies>dependencyManagement
在dependencyManagement下申明的dependencies,Maven并不会去实际下载所依赖的jar包,而是在dependencyManagement中用一个Map记录了jar的三维坐标。
而仅仅是被dependencies包裹的元素,Maven是会去仓库实际下载所需要的jar包的,而至于需要下载什么
版本的jar包就有两种判断途径:
1:如果dependencies里的dependency自己没有声明version元素,那么maven就会倒dependencyManagement里面去找有没有进行过版本声明,如果有,就继承它,如果没有就会报错,告诉你必须为dependency声明一个version
2:如果dependencies中的dependency声明了version,那么无论dependencyManagement中有无对该jar的version声明,都以dependency里的version为准。
dependencies与dependencyManagement的依赖继承
- dependencies即使在子项目中不写该依赖项,那么子项目仍然会从父项目中继承该依赖项(全部继承)。dependencyManagement里只是声明依赖,并不实现引入,因此子项目需要显示的声明需要用的依赖。
如果不在子项目中声明依赖,是不会从父项目中继承下来的;
- 只有在子项目中写了该依赖项,并且没有指定具体版本,才会从父项目中继承该项,并且version和scope都读取自父pom;
- 如果子项目中指定了版本号,那么会使用子项目中指定的jar版本。
常用命令汇总
命令
| 说明
|
mvn archetype:generate -DgroupId=packageName -DartifactId=projectName -Dversion=x.x-SNAPSHOT | 创建Maven项目
|
mvn compile
| 编译源代码
|
mvn test-compile
| 编译测试代码
|
mvn test
| 运行测试
|
mvn package
| 打包
|
mvn -Dtest package clean install -Dmaven.test.skip=true
| 打包但不测试
|
mvn install
| 在本地库repository中安装该项目的jar包
|
mvn clean
| 清除产生的项目
|
mvn eclipse:eclipse | 生成eclipse项目
|
mvn idea:idea | 生成idea项目
|
scope 标签
Dependency scope 是用来限制 Dependency 的作用范围的, 影响maven项目在各个生命周期时导入的package的状态。
编号
| 参数
| 说明
|
编号
| 参数
| 说明
|
1
| compile | 默认的scope,表示 dependency 可以在所有生命周期中使用。而且这些 dependencies 会传递到依赖的项目中。适用于所有阶段,会随着项目一起发布(会将依赖的项目打成jar包,放入本项目的Lib里)
|
2
| provided | 表明了dependency 由JDK或者容器提供。期望JDK、容器或使用者会提供这个依赖。如servlet.jar。这个scope 只能作用在编译和测试时(编译需要而发布不需要),同时没有传递性(不会将包打入本项目中,只是依赖过来)。
|
3
| runtime | 表示dependency不作用在编译时,但会作用在运行和测试时,如JDBC驱动,适用运行和测试阶段。 比如在测试别的功能的阶段,还没有编写mysql,所以编译的时候不会用到这个包。即使加了这个依赖,但是由于编译的时候没有用到,那么生成的target下的lib中是没有对应的jar包的。所以标记runtime
|
4
| test | 表示dependency作用在测试时,不作用在运行时。 只在测试时使用,用于编译和运行测试代码。不会随项目发布。
|
5
| system | 跟provided 相似,但是在系统中要以外部JAR包的形式提供,maven不会在repository查找它。
|
6
| import | 只使用在 <dependencyManagement> 中,表示从其它的 pom 中导入 dependency 的配置 |
一、基本原则
1、依赖路径最短优先原则
一个项目Demo依赖了两个jar包,其中A-B-C-X(1.0) , A-D-X(2.0)。由于X(2.0)路径最短,所以项目使用的是X(2.0)。
2、pom文件中申明顺序优先
如果A-B-X(1.0) ,A-C-X(2.0) 这样的路径长度一样怎么办呢?这样的情况下,maven会根据pom文件声明的顺序加载,如果先声明了B,后声明了C,那就最后的依赖就会是X(1.0)。
3、同一个pom明确版本
如果在A工程中先依赖了junit4.9的版本, 然后下面又在依赖了junit4.8 的版本, 那么,按照就近原则,越下面越近, 最后使用的是junit4.8 的版本.
总结: maven的依赖如果发生了冲突, 服从就近原则.
4、优先级
本pom版本(覆写优先)> 父pom版本 > dependencyManagement版本 > 间接依赖版本(申明优先)
可选依赖
假如项目 A 存在这样的依赖关系,A->B、B->X(可选)、B->Y(可选optional:标记依赖是否可选),依赖将不会得以传递,即 X、Y 将不会对 A 有任何影响。
在理想的情况下,是不应该使用可选依赖的。使用可选依赖的原因是某一个项目实现了多个特性,但违背了单一指责原则。
备注:调用方的jar版本必须大于服务方要求的jar版本,否则可能会报出java.lang.NoSuchMethodError
二、依赖复用:消除多模块依赖配置重复
1、继承
POM中配置了继承,当前模块与父模块使用同样的groupId和version时,就可以将<groupId>和<version>元素删除,因为它们可以从父模块继承而来,重复配置没有什么意义。
<parent>
<groupId>com.example</groupId>
<artifactId>fun-parent</artifactId>
<version>1.0.6</version>
</parent>
2、dependencyManagement
问题:在父模块中配置dependencies,那样所有子模块都自动继承,但这么做是有问题的,例如你将模块C的依赖spring-aop提取到了父模块中,但模块A和B虽然不需要spring-aop,但也直接继承了。
dependencyManagement就没有这样的问题,dependencyManagement只会影响现有依赖的配置,但不会引入依赖。
这样不会给任何子模块引入依赖,但如果某个子模块需要使用JUnit和Log4j的时候,我们就可以简化依赖配置。
3、特殊情况
在配置项目依赖的时候,我们也应当一直显式地写明依赖版本,以避免Maven在不同的时刻引入不同版本的依赖而导致项目构建的不稳定。
三、增还是删
有时候这个答案是很显然的,当你的POM中存在一些依赖或者插件配置,但实际代码没有用到这些配置的时候,应该尽早删掉它们以免给人带来困惑。
第二个不常用的方法就是创建一个空包。空包的坐标和你需要隔离的Jar包坐标一样,deploy的公司的私服上面。项目中这个空包申明在pom文件靠前的地方,这样依据maven依赖原则,这个空包会优先被使用,后面所有无论是直接依赖还是间接依赖的相同坐标的jar包都不会被使用了。空包比exclusion的好处就是不用在所有间接依赖的地方去exclusion。
<exclusions>
<exclusion>
<groupId>com.ibm.icu</groupId>
<artifactId>icu4j</artifactId>
</exclusion>
</exclusions>