前文(Maven填坑记一)以持续集成为例,引申出了maven命令的正确使用方法,并简单介绍了一下maven的基本概念。今天我们从一个具体的pom文件入手来了解一下maven的其他概念以及面试中常见的一些问题。
Maven的依赖
<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> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.0.RELEASE</version> <relativePath/> </parent> <groupId>com.bancheng</groupId> <artifactId>demoTest</artifactId> <version>0.0.1-SNAPSHOT</version> <name>demoTest</name> <description>Demo project for Spring Boot</description> <properties> <spring.version>4.2.1</spring.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <version>${spring.version}</version> <scope>test</scope> </dependency> </dependencies> </project>
上面是一个标准的pom文件,其中的坐标部分,前文已经涉及过,今天就不展开了,我们来重点关于一下<dependencies></dependencies>包含的内容。这部分内容在maven中被称为依赖,定义了该项目需要引入的外部jar包,在实际构建过程中,会遵循上文的依赖搜索顺序,将相应的jar包打入最终的项目jar包或者war包里面。
接下来让我们看一下依赖可能涉及的具体元素吧。
- dependencies:一个 pom.xml 文件中只能存在一个这样的标签。用来管理依赖的总标签。
- dependency:包含在dependencies标签中,可以有无数个,每一个表示一个依赖
- groupId,artifactId和version:依赖的基本坐标,对于任何一个依赖来说,基本坐标是最重要的,maven根据坐标才能找到需要的依赖。如果一个依赖在项目中需要引用多次,对于其版本定义可以采用变量的方式,具体请参考上面。
- type:依赖的类型,对应于项目坐标定义的packaging。大部分情况下,该元素不必声明,其默认值是jar。
- scope:依赖的范围,默认值是 compile。
- optional:标记依赖是否可选。
- exclusions:用来排除传递性依赖。
项目中多次引用相同的类包,版本相同,可以采用上面变量的方式统一声明,那如果版本不相同呢,特别是不同类包的间接依赖导致的版本不一致,maven是如何解决对应版本冲突的呢?
这就必然需要定义一个依赖传递规则。
1.第一声明者优先原则
2.路径近者优先原则
可以通过mvn dependency:tree命令生成项目的依赖树,如下图所示:
该依赖树的根节点是本项目生成的jar包,接下来是本项目直接依赖的类包,其他是本项目直接依赖类包依赖的间接类包,这三者形成了一个不同层级的树状结构。依据上面的两个原则,如果发生冲突的类包是在同一层级,那么优先声明的类包版本生效,如果是在不同层级的类包,那么离根节点层级更近的类包版本生效。当然这种方式不够直观,最好的方法还是通过exclusions标签来显示排除,如下图所示。
<exclusions> <exclusion> <artifactId>unitils-database</artifactId> <groupId>org.unitils</groupId> </exclusion> </exclusions>
Maven的依赖范围
上面的依赖中用到的scope标签的值是test,这个有什么特殊含义呢?
1. compile:编译依赖,默认的依赖方式,在编译(编译项目和编译测试用例)、运行测试用例、运行(项目实际运行)三个阶段都有效。
2. test:测试依赖,只在编译测试用例和运行测试用例有效,典型的有JUnit。
3. provided:对于编译和测试有效,不会打包进发布包中,典型的例子为servlet-api,一般的web工程运行时都使用容器的servlet-api,如tomcat容器。
4. runtime:只在运行测试用例和实际运行时有效,典型的是jdbc驱动jar包。
5. system: 不从maven仓库获取该jar,而是通过systemPath指定该jar的路径。
6. import: 用于一个dependencyManagement对另一个dependencyManagement的继承。
常见的面试题
Maven常见的面试题有哪些呢?
Q:dependencies和dependencyManagement,plugins和pluginManagement有什么区别呢?
A:dependencyManagement是表示依赖jar包的声明,即使在项目中的dependencyManagement下声明了依赖,maven不会直接加载该依赖,但是其声明可以被继承。一个常用的使用案例是父子项目的时候,应用于父项目。之后在在子项目中的dependencies节点可以只配置groupid和artifactid就可以完成类包的引用。类似的还有plugins和pluginManagement。
Q:如何制定JDK版本
A:有两种方式,一种是通过properties方式,如下面所示:
<properties> <maven.compiler.source>1.8<maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <properties>
一种是使用maven-compiler-plugin插件,并制定source和target版本,如下面所示:
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactedId>maven-complier-plugin</artifactedId> <version>3.3</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build>
Q:如何避免将dependency打包到构件中
A:如上文所述,将<scope>标签的值设为provided,只会在编译和测试阶段生效。
以上介绍了依赖的传递规则、范围及常见的面试题,希望对大家有帮助,更多精彩,请持续关注哦。