Maven依赖范围
之前我们说过,maven坐标能够确定一个项目。换句话说,我们可以用它来解决依赖关系。在POM中,依赖关
系是在dependencies部分中定义的。在上面的POM例子中,我们用dependencies定义了对于junit的依赖:
pom.xml配置文件代码:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
那这个例子很简单,但是实际开发中我们会有复杂得多的依赖关系,因为被依赖的jar文件会有自己的依赖关系。
那么我们是不是需要把那些间接依赖的 jar文件也都定义在POM中呢?答案是不需要,因为maven提供了传递依赖的
特性。
所谓传递依赖是指maven会检查被依赖的jar文件,把它的依赖关系纳入最终解决的依赖关系链中。在POM的
dependencies部分中,scope决定了依赖关系的适用范围。Maven提供了三种classpath:编译、测试和运行。我们
的例子junit的scope是test,那么它只会在执行 compiler:testCompile and surefire:test目标的时候才会被加到
classpath中,在执行compiler:compile目标时是拿不到junit的。
我们还可以指定scope为provided,意思是JDK或者容器会提供所需的jar文件。比如说在做web应用开发的时
候,我们在编译的时候需要servlet API jar文件,但是在打包的时候不需要把这 jar文件打在 WAR 中,因为servlet容
器或者应用服务器会提供的。
scope的默认值是compile,即任何时候都会被包含在classpath中,在打包的时候也会被包括进去。
<scope>元素表示这个依赖包的范围,有六个值:
1.compile:默认的范围,在编译和打包时都会将依赖存储进去 ;
2.provided:在编译和测试的过程有效,最后生成war包时不会加入,诸如:servlet-api,因为servlet-api,
tomcat等web服务器已经存在了,如果再打包会冲突 ;
3.runtime:只在运行的时候依赖,在编译的时候不依赖 ;
4.test:范围指的是测试范围有效,在编译和打包时都不会使用这个依赖 ;
5.system:与本机系统相关联,可移植性差;
6.import:导入的范围,它只使用在<dependencyManagement>中,表示从其它的pom中导入
<dependency>的配置。
Maven传递依赖
我们假设项目demo2依赖项目demo1,项目demo3依赖项目demo2,那么项目demo3对项目demo1的依赖就
是传递依赖。
我们来实现这个例子:
首先建立三个Maven项目:demo1、demo2和demo3 :
因为demo2依赖demo1,因此先在demo2中加入demo1的依赖,也就是在demo2的POM中添加如下代码:
<dependency>
<groupId>com.demo</groupId>
<artifactId>demo1</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
我们编译项目demo2,出现以下的错误:
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building demo2 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[WARNING] The POM for com.demo:demo1:jar:0.0.1-SNAPSHOT is missing, no dependency information available
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.389 s
[INFO] Finished at: 2016-12-29T19:23:42+08:00
[INFO] Final Memory: 7M/71M
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal on project demo2: Could not resolve dependencies for project com.demo:demo2:jar:0.0.1-SNAPSHOT: Could not find artifact com.demo:demo1:jar:0.0.1-SNAPSHOT -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/DependencyResolutionException
上面出现错误是我们找不到项目demo2依赖的demo1的jar包,因为在本地仓库和远程仓库中都没有,因此报
错,这就需要我们手动打包demo1成为jar包加入到本地仓库。我们先来打包demo1成jar包加入到本地仓库供
demo2使用。
这回我们再次编译demo2就会成功:
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building demo2 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ demo2 ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory E:\Java\JavaEE\Demo\demo2\src\main\resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ demo2 ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to E:\Java\JavaEE\Demo\demo2\target\classes
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.540 s
[INFO] Finished at: 2016-12-29T19:41:22+08:00
[INFO] Final Memory: 13M/129M
[INFO] ------------------------------------------------------------------------
编译成功后还是打包demo2下喜爱那个木加入到本地仓库供demo3项目使用。
因为demo3依赖demo2,因此先在demo3中加入demo2的依赖,也就是在demo3的POM中添加如下代码:
<dependency>
<groupId>com.demo</groupId>
<artifactId>demo3</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
这一次我们demo3成功编译。
我们看看各个项目的依赖管理下的依赖项和依赖树:
排除依赖传递
假设demo3只是依赖demo2,不依赖demo1,这就用到了排除依赖传递,那么我们需要在demo3中的POM中
加入如下的代码:
<dependency>
<groupId>com.demo</groupId>
<artifactId>demo2</artifactId>
<version>0.0.1-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>com.demo</groupId>
<artifactId>demo1</artifactId>
</exclusion>
</exclusions>
</dependency>
那么我们再看demo3项目的依赖管理不会看到demo1
Maven依赖冲突
Maven依赖冲突的有两个原则:
1.短路优先
A->B->C->X(jar)
A->D->X(jar)
那么项目A会使用D的jar包的版本。
假设我们的项目demo1依赖的如下的jar版本的包:
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
demo2依赖如下的jar版本的包:
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.0</version>
</dependency>
demo3则使用demo2的依赖:
2.先声明先优先
如果路径长度相同,则谁先声明,先解析谁。
假如我们的项目demo3同时依赖demo1和demo2,在上面的例子中我们先将demo2依赖demo1的依赖删除。
在demo3中同时加入demo1和demo2的依赖,如下面的代码:
<dependency>
<groupId>com.demo</groupId>
<artifactId>demo1</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.demo</groupId>
<artifactId>demo2</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
我们看依赖的版本:
关于Maven依赖管理就说这么多。