Maven依赖分析

依赖结构

之前提到可以用maven 命令查看项目的依赖结构,比如输入一下命令

mvn dependency:tree
maven 就把我们项目依赖结果输出成树的结构。

[INFO] +- thirdparty_tools:Spring:pom:3.0.5_full:compile
[INFO] |  +- thirdparty_lib:org.springframework.aop:jar:3.0.5_full:compile
[INFO] |  +- thirdparty_lib:org.springframework.asm:jar:3.0.5_full:compile
[INFO] |  +- thirdparty_lib:org.springframework.aspects:jar:3.0.5_full:compile


声明

上面的输出涉及到几个概念。下面简单说下。
一般情况下我们会在 pom.xml 中设置下面类似的依赖。
		<dependency>
			<groupId>thirdparty_lib</groupId>
			<artifactId>commons-jexl</artifactId>
			<version>2.1.1</version>
			<type>jar</type>
			<scope>compile</scope>
			<exclusions>
				<exclusion>
						<groupId>commons-logging</groupId>
						<artifactId>commons-logging</artifactId>
					</exclusion>
				</exclusions>
		</dependency>
groupId、artifactId 和 version 很简单啦,就是一个jar包的坐标,maven就是根据这个信息在本地仓库或远程仓库中找到指定的jar包的。
type : 依赖类型。大部分情况下不需要这个声明,默认jar。
scope : 依赖范围。
exclusions : 用来排除传递依赖。
其实,大部分依赖我们只需要声明坐标即可。


下面说下几个概念。

classpath

相信很多人对这个不默认吧。用于告诉Java执行环境,在哪些目录下可以找到您所要执行的Java程序所需要的类或者包。简单的来说就是一些包和类的集合。
其实maven在帮我们完成工作时,不同时期的classpath有所不同。

1. Maven在编译项目主代码的时候需要使用一套 classpath。 
2. 之后 Maven在编译和执行测试的时候会使用另外一套 classpath。
3. 最后实际运行 Maven项目的时候,又会使用一套 classpath.
也就是说我们在使用Maven时,一共会涉及三套 classpath。


依赖范围

上面 dependency 里面 scope 的声明就是指名 依赖范围。
而依赖范围就是用来控制依赖的当前jar包与这三种 classpath (编译classpath,测试 classpath,运行classpath) 的关系。


依赖范围有如下几种
compile:编译依赖范围。默认使用。对于三种classpath都有效。spring-core
test:测试依赖范围。只对 测试classpath有效。编译代码或运行时无法使用此类依赖。JUnit。编译测试代码和运行测试的时候需要。
provided:已提供依赖范围。即运行环境所在容器已经提供。可知 对于 编译和测试classpath有效。运行时无效。 servlet-api.
runtime:运行时依赖。该范围的依赖对于测试运行classpath有效,对于编译主代码无效。 JDBC驱动。我们知道编译代码只需要JDK的JDBC接口即可。而JDBC驱动是上述接口的实现。所以执行测试或运行项目时才会需要。
system:系统依赖范围。其支持的classpath与provided依赖范围一致。只是不通过maven仓库解析。而是通过systemPath 元素显示指定依赖文件的路径。因此为了系统构建可以多处运行,不建议使用此依赖范围。

		<dependency>
			<groupId>javax.sql</groupId>
			<artifactId>jdbc-stdext</artifactId>
			<version>2.0</version>
			<scope>system</scope>
			<optional></optional>
			<systemPath>${java.home}/lib/rt.jar</systemPath>
		</dependency>
综合上述,依赖范围与 classpath 的关系如下:

依赖范围 对编译classpath有效 对测试classpath有效 对运行时classpath有效 举例
compile Y Y Y spring-core
test   Y   JUnit
provided Y Y   servlet-api
runtime   Y Y JDBC驱动的实现,如oracle_jdbc.jar
system Y Y   本地环境变量中的jar

传递性依赖

我们知道,很多jar包都是有依赖的,比如 A 依赖 B,而B依赖 C,也就是我们所说的A 间接依赖 C。即 A => B => C。此时 C就是 A的一个传递性依赖。在项目中,我们不需要考虑传递性依赖,maven的传递性依赖会解析我们的间接依赖,并将它们引入我们的项目。


这里说下,依赖范围对传递性依赖是有影响的。当依赖关系是 A => B => C 时,我们假定 A => B 为第一依赖,B => C为第二依赖。这里将它们列成表格,第一列是第一依赖,第一行是第二依赖。因此就会组合成如下传递性依赖范围的范围。


  compile test provided runtime
compile compile     runtime
test test     test
provided provided   provided provided
runtime runtime     runtime
空白意味不依赖

下面举个例子,下面是两个Project,mavenprjA 和 mavenprjB。各自 pom.xml如下

mavenprjA 

<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/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com</groupId>
	<artifactId>mavenprjA</artifactId>
	<packaging>jar</packaging>
	<version>0.0.1-SNAPSHOT</version>
	<name>my Maven Webapp</name>
	<url>http://maven.apache.org</url>
	<dependencies>
		<dependency>
			<groupId>com</groupId>
			<artifactId>mavenprjB</artifactId>
			<version>0.0.1-SNAPSHOT</version>
			<scope>test</scope><!------------------A ---------------->
		</dependency>

		<!-- Test start -->
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.12</version>
			<scope>test</scope>
		</dependency>
		<!-- Test end -->
	</dependencies>
</project>


mavenprjB

<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/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com</groupId>
	<artifactId>mavenprjB</artifactId>
	<packaging>jar</packaging>
	<version>0.0.1-SNAPSHOT</version>
	<name>my Maven Webapp</name>
	<url>http://maven.apache.org</url>
	<dependencies>
		<!-- Util -->
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-lang3</artifactId>
			<version>3.4</version>
			<scope>test</scope><!------------------B ---------------->
		</dependency>
	</dependencies>
</project>

从上面可以发现, mavenprjA => mavenprjB, 而 mavenprjB => org.apache.commons.commons-lang3, 依赖范围都是 test。 查看 mavenprjA 的依赖树。

从下面输出看,当我们设置第一依赖scope 为test 时, mavenprjA的依赖树中 ,mavenprjB下是 没有commons-langs 的 即是不传递的。

[INFO] ------------------------------------------------------------------------
[INFO] Building my Maven Webapp 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-dependency-plugin:2.1:tree (default-cli) @ mavenprjA ---
[INFO] com:mavenprjA:jar:0.0.1-SNAPSHOT
[INFO] +- com:mavenprjB:jar:0.0.1-SNAPSHOT:test
[INFO] \- junit:junit:jar:4.12:test
[INFO]    \- org.hamcrest:hamcrest-core:jar:1.3:test
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

然后我们不停地改变A、B 两处依赖的 scope,然后查看依赖树。就可以验证了。

猜你喜欢

转载自blog.csdn.net/hustzw07/article/details/78132041