1.以Spring-Boot的pom.xml为例
<?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>
<!--指定了当前pom的版本-->
<!--坐标信息 start-->
<groupId>com.antfin</groupId><!--主项目标识 反写的公司网址+项目名-->
<artifactId>maven</artifactId><!--项目名+模块名-->
<version>0.0.1-SNAPSHOT</version>
<!--第一个0表示大版本号
第二个0表示分支版本号
第三个0标识小版本号
0.0.1SNAPSHOT
snapshot快照
alpha内测
beta公测
Release稳定
GA正式发布
-->
<packaging>jar</packaging>
<!--打包方式:默认是jar
war zip pom
-->
<!--坐标信息 end-->
<!--项目描述名-->
<name>hi</name>
<!--项目地址-->
<url>http://maven.apache.org</url>
<!--项目描述-->
<description>Demo project for Spring Boot</description>
<!--开发人员列表-->
<developers></developers>
<!--许可证信息-->
<license></license>
<!--组织信息-->
<organization></organization>
<parent>
<!--继承此获得合理的默认配置,比如版本信息,其他依赖的不需要写版本信息-->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<!--默认版本和Java版本-->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<!--依赖项-->
<dependencies>
<dependency>
<!-- 指定依赖坐标 -->
<!--版本由parent指定-->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<!--插件列表-->
<plugins>
<plugin>
<!--坐标-->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2.具体标签解析
以一个具体的依赖为例
<!--依赖项-->
<dependencies>
<dependency>
<!-- 指定依赖坐标 -->
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope><!-- 指定依赖范围 test指在test中使用 -->
<type></type>
<!-- 设置依赖是否可选 默认为false,继承,为true显示引入该依赖-->
<optional></optional>
<!-- 排除依赖传递列表 -->
<exclusions>
<exclusion>
</exclusion>
</exclusions>
</dependency>
</dependencies>
根元素下project下的dependencies可以包含一个或者多个dependency元素,以声明一个或者多个项目依赖。每个依赖可以包含的元素有:
- groupId,artifactId和version:依赖的基本坐标,对于任何一个依赖来说,基本坐标是最重要的,Maven根据坐标才能找到需要的依赖。
- type:依赖的类型,对应于项目坐标定义的packaging。大部分情况下,该元素不必声明,其默认值是jar。
- scope:依赖的范围,下面会进行详解。
- optional:标记依赖是否可选。
- exclusions:用来排除传递性依赖
大部分依赖声明只包含基本坐标
2.1依赖范围
Maven在编译主代码的时候需要使用一套classpath,在编译和执行测试的时候会使用另一套classpath,实际运行项目的时候,又会使用一套classpath。
<scope>
标签的意思就是当前插件依赖的范围 在开发中我们把第三方jar放入到classpath中,就能够调用第三方的类和方法。
- maven有三种classpath :编译、测试、运行
<scope>test</scope><!--控制依赖与三种classpath的关系-->
1. compile 默认,编译测试运行都有效
2. provided 编译和测试时有效,最后是在运行的时候不会被加入。官方举了一个例子。比如在JavaEE web项目中我们需要使用servlet的API,但是呢Tomcat中已经提供这个jar,我们在编译和测试的时候需要使用这个api,但是部署到tomcat的时候,如果还加入servlet构建就会产生冲突,这个时候就可以使用provided。
3. runtime 测试和运行,典型的例子是JDBC驱动实现,项目主代码的编译只需要JDK提供的JDBC接口,只有在执行测试或者运行项目的时候才需要实现上述接口的具体JDBC驱动。
4. test 只有测试 Junit
5. system 与本机系统相关联,可移植性差,编译和测试时有效
6. import 导入范围,只在使用dependencyManagement中表示从其他pom导入dependency
举个import的例子,解释:就是把A中的构建导入到B中。
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>maven</groupId>
<artifactId>B</artifactId>
<packaging>pom</packaging>
<name>B</name>
<version>1.0</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>maven</groupId>
<artifactId>A</artifactId>
<version>1.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
2.2依赖传递
在正常开发中,我们也会封装maven构建供小伙伴们使用,在我们使用自定义的构建中,那么我们自定义的构建中依赖的构建,也会依赖传递过来。
实例:
分别新建三个项目:project1,project2,project3.
把project1打包安装到本地仓库中
mvn clean install
在project2中依赖project1
<dependency> <groupId>com.antfin</groupId> <artifactId>project1</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency>
把project2打包安装在本地仓库
在project3中依赖project2
<dependency> <groupId>com.antfin</groupId> <artifactId>project2</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency>
然后编译项目project3,在Maven dependencies会发现project1也在其中
即project1通过project2传递到了project3中
说明
假设A依赖于B,B依赖于C,我们说A对于B是第一直接依赖,B对C是第二直接依赖,A对于C是传递性依赖。第一直接依赖的范围和第二直接依赖的范围决定了传递性依赖的范围。
最左边一行表示第一直接依赖范围,最上面一行表示第二直接依赖范围,中间的交叉单元格则表示传递性依赖范围。
compile | test | provided | runtime | |
---|---|---|---|---|
compile | compile | runtime | ||
test | test | test | ||
provided | provided | provided | provided | |
runtime | runtime | runtime |
仔细观察上面表格,我们发现这样的规律:
- 当第二直接依赖的范围是compile的时候,传递性依赖的范围与第一直接依赖的范围一致;
- 当第二直接依赖的范围是test的时候,依赖不会得以传递;
- 当第二直接依赖的范围是provided的时候,只传递第一直接依赖的范围也为provided的依赖,切传递性依赖的范围同样为provided;
- 当第二直接依赖的范围是runtime的时候,传递性依赖的范围与第一直接依赖的范围一致,但compile例外,此时传递性依赖的范围为runtime。
2.3排除依赖
上面演示了传递依赖,但是如果我们只需要依赖project02 并不想依赖进来project01怎么办呢?这个时候就要是用exclusions 排除依赖列表
如下配置:
<dependency>
<groupId>com.antfin</groupId>
<artifactId>project2</artifactId>
<version>0.0.1-SNAPSHOT</version>
<exclusions>
<exclusion>
<!--排除的依赖,依赖坐标不加version-->
<groupId>com.antfin</groupId>
<artifactId>project1</artifactId>
</exclusion>
</exclusions>
</dependency>
这样在project3中就不会看到依赖01了。
2.4maven对传递性依赖的处理
有些依赖,maven会对其按照下述原理自动处理
- 短路优先:谁离得最近就使用谁的依赖jar包
C到达A为C->B->A
C到达B为C->B
例如:
A中的 commons-io的版本为2.4
B中的 commons-io的版本为2.0
C中依赖于B,B依赖于A
则C的junit的包为2.0版本
因为依赖的短路优先
- 如果两条路都是一样长的时候呢?
如果路径长度相同,则谁先声明,先解析谁
C到达A为C->A
C到达B为C->B
则看pom文件中依赖的两个工程谁在前面就是用哪个版本
2.5Maven依赖jar包冲突解决
1.判断jar是否正确的被引用
在项目启动时加上VM参数:-verbose:class
项目启动的时候会把所有加载的jar都打印出来 类似如下的信息:
classpath加载的jar
具体load的类
我们可以通过上面的信息查找对应的jar是否正确的被依赖,具体类加载情况,同时可以看到版本号,确定是否由于依赖冲突造成的jar引用不正确;
通过maven自带的工具:
mvn dependency:tree
Myeclipse或者idea或者eclipse中用pom编辑器打开一个pom文件,在Dependency Hierarchy的Tab页中,就可以查看当前pom文件中显示声明的jar包,及这些显示声明的jar中隐式引入的依赖jar包。
2.6Maven中jar,pom,war的区别
pom打出来可以作为其他项目的maven依赖,比如你写了两个工程,工程a写了一些工具类,然后你可以把它打成pom,再在工程b里面添加a的依赖,然后你在工程b中就可以使用a定义好的工具类。
jar是maven出来之前三方库普遍使用的方式,比较常见的是jdbc驱动包,一般从官网下载下来的都是jar文件。也可以打成可执行文件,类似.exe那样的,用命令java -jar xxx.jar执行。
war常用于java web工程,但也不是绝对的,war也可以用java -jar xxx.war执行,前提是你有配置main函数入口
父项目中打包方式必须是pom 如
<packaging>pom</packaging>
,父项目中使用<modules><module>msite-base</module></modules>
指定子项目子项目中使用
<parent>
指定,子项目继承父项目的大部分属性
2.7插件和依赖的区别
- 插件是一种工具,例如compile插件是用来编译代码的工具,mybatis插件是用来自动生成数据库dao和mapper的工具。
- 而依赖则是项目工程在编译过程中需要依赖的二方及三方包。在你的工程中可以不需要mybatis插件,自己去实现sql的crud,但如果工程里需要三房包,则必须要用dependency引入。