今天我们分享一下Maven中的依赖范围。因为在之前的Maven实战-第四篇(Maven的依赖配置)
文章中已经对一部分内容进行了详细的介绍,所以本篇中涉及到上述的内容将不在过多的介绍了。
一、一般依赖范围
在Maven中依赖范围的目的控就是控制项目中classpath的关系。在Maven中主要有三种classpath。分别是编译的classpath、测试的classpath和运行的classpath。下面我们看一下不同依赖范围对上述三种classpath的影响。
1、compile
编译依赖范围,也是Maven默认的依赖范围。使用此依赖范围时表示,此组件对编译、测试和运行都有效。所以此依赖范围,对于上面的三个classpath都是有效的。
2、test
测试依赖范围。使用此依赖范围表示,此组件只对测试的classpath有效。对编译的和运行时无法使用此依赖。比较典型的组件为:Junit组件,具体配置如下:
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
因为Maven实战-第四篇(Maven的依赖配置)
文章已经详细介绍过了,所以这里就不在进行演示了。
3、provided
使用此依赖范围。组件将对编译和测试有效,而对运行时无效。比较典型的组件为:servlet-api。此组件我们编译和测试的时需要依赖,但在运行时,由于应用服务器已经提供了此组件的支持,所以不需要我们在项目中添加此依赖了。同样的因为之前文章中已介绍过了,所以这里就不在过多介绍了。
4、runtime
运行时依赖范围。也就是只对运行时才有效,但对测试也是有效的。典型的组件为数据库的驱动。只有项目运行时才需要。对其它的classpath是无效的。
5、system
系统依赖范围。system依赖范围和provided的依赖范围一致。唯一不同的之处就是在使用system依赖范围时,我们需要指定systemPath参数。因为system依赖范围并不是通过Maven仓库解析的,而是通过systemPath参数指定的路径解析的。具体的配置如下:
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>system</scope>
<systemPath>/Users/jilinwula/.m2/repository/junit/junit/4.13.2/junit-4.12.jar</systemPath>
</dependency>
比较典型的场景为加密项目中可能会使用。它会读取我们指定目录中的依赖包加载到项目中。例如从外部设备中读取。
6、import
导入依赖范围。此依赖范围和其它的依赖不同,它并不会影响编译、测试和运行的classpath。而是通过此依赖范围,将其它项目的依赖引入本项目中。所以在使用此依赖时,需要引入的依赖类型必须为pom类型。
下面是依赖范围和classpath之间的关系。
依赖范围(scope) | 编译(classpath) | 测试(classpath) | 运行(classpath) | 例子 |
---|---|---|---|---|
compile | Y | Y | Y | spring-core |
test | - | Y | Y | junit |
provided | Y | Y | - | servlet-api |
runtime | - | Y | Y | JDBC驱动 |
system | Y | Y | - | 本地的组件 |
横轴表示classpath,纵轴则表示Maven中的依赖范围。表示Y
则表示对该classpath有效果,反之-
则表示无效。
二、传递性依赖范围
上面介绍的是一般的依赖范围。但我们知道也就是上篇中介绍的,Maven中有传递性依赖,那么这个依赖会不会和一般的依赖范围有些不同呢。答案是肯定的。在Maven中依赖范围不仅可以控制依赖与classpath的关系,还对传递性依赖会产生影响。下面内容是依赖范围影响传递性依赖的关系:
compile | test | provided | runtime | |
---|---|---|---|---|
compile | compile | - | - | runtime |
test | test | - | - | test |
provided | provided | - | provided | provided |
runtime | runtime | - | - | runtime |
在Maven中如果项目A依赖于B,项目B又依赖于项目C,我们会说A对于B是第一直接依赖,B对于C是第二直接依赖,而A对于C则是传递性依赖。也就早篇中介绍的内容。所以在上面的表格中最左边的一列表示第一直接依赖范围,最上面一行表示的是第二直接依赖范围,中间的交叉单元格则表示传递性依赖的范围。
下面我们通过一个简单的例子来解释一下上面说的内容。假如我们有一个项目叫做account-email。该项目有一个com.icegreen:greenmail:1.3.1b的直接依赖,我们说这是第一直接依赖,并且假如该依赖范围是test。而greenmail又有一个javax.mail:mail:1.4的直接依赖,我们说这是第二直接依赖,其依赖范围是compile。显然javax.mail:mail:1.4是account-email的传递性依赖。按照上图所示,当第一直接依赖范围为test,第二直接依赖范围是compile的时候,传递性依赖的范围是test,因此javax.mail:mail:1.4是account-email的一个范围是test的传递性依赖。
我们通过下面的例子来验证一下上面说的内容。为了能够直观的观察第一依赖与第二依赖以及传递性依赖的关系。我们创建三个项目然后依次依赖,来验证上面说的内容。
1、mazhe-maven-user
- pomx.ml
<?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">
<parent>
<artifactId>midai-mazhe-maven</artifactId>
<groupId>cn.ma-zhe</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>mazhe-maven-user</artifactId>
</project>
- MazheMavenUser
public class MazheMavenUser {
public static void main(String[] args) {
System.out.println("Hello World Mazhe Maven User!");
}
}
2、mazhe-maven-order
- 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">
<parent>
<artifactId>midai-mazhe-maven</artifactId>
<groupId>cn.ma-zhe</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>mazhe-maven-order</artifactId>
<dependencies>
<dependency>
<groupId>cn.ma-zhe</groupId>
<artifactId>mazhe-maven-user</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
- MazheMavenOrder
public class MazheMavenOrder {
public static void main(String[] args) {
System.out.println("Hello World Mazhe Maven Order!");
}
}
3、mazhe-maven-mall
- 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">
<parent>
<artifactId>midai-mazhe-maven</artifactId>
<groupId>cn.ma-zhe</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>mazhe-maven-mall</artifactId>
<dependencies>
<dependency>
<groupId>cn.ma-zhe</groupId>
<artifactId>mazhe-maven-order</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
- MazheMavenMall
public class MazheMavenMall {
public static void main(String[] args) {
System.out.println("Hello World Mazhe Maven Mall!");
}
}
我们创建了三个项目MazheMavenUser、mazhe-maven-order、mazhe-maven-mall
。期中项目mazhe-maven-mall
依赖于项目mazhe-maven-order
,项目mazhe-maven-order
又依赖于项目MazheMavenUser
,所以项目MazheMavenUser
就是项目mazhe-maven-mall
的传递性依赖。下面我们按照上面的依赖关系来验证一下,当第一依赖是compile
,第二依赖也是compile
时,传递性依赖是不是也是compile
的依赖范围。因为Maven中compile
依赖范围为默认的依赖范围,所以我们不需要修改任何pom.xml
直接在项目中验证一下。因为我们项目中引入了mazhe-maven-order
依赖。所以因为传递性依赖的关系,Maven也会引入MazheMavenUser
依赖。所以我们直接在MazheMavenMall中调用这两个项目的代码看看是否能成功的引用。
compile&compile
- MazheMavenMall
public class MazheMavenMall {
public static void main(String[] args) {
System.out.println("Hello World Mazhe Maven Mall!");
MazheMavenOrder.main(args);
MazheMavenUser.main(args);
}
}
*输出
Hello World Mazhe Maven Mall!
Hello World Mazhe Maven Order!
Hello World Mazhe Maven User!
我们看上面输出的日志,成功的验证了当第一依赖为compile
,并且第二依赖也是compile
的时候,传递性依赖的范围也是compile
。
compile | |
---|---|
compile | compile |
compile&test
下面我们验证一下,当然第一依赖范围为compile
,第二依赖范围为test
的时候,传递性依赖的范围。因为是要修改的第二依赖范围,所以我们需要将mazhe-maven-order项目中的依赖范围修改为test
。
- 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">
<parent>
<artifactId>midai-mazhe-maven</artifactId>
<groupId>cn.ma-zhe</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>mazhe-maven-order</artifactId>
<dependencies>
<dependency>
<groupId>cn.ma-zhe</groupId>
<artifactId>mazhe-maven-user</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
我们再次看一下mazhe-maven-mall项目中,是否有问题。
如上图所示,我们看到MazheMavenMall类中的调用MazheMavenUser代码报错,这是因为没有找到这个类的依赖所至。下面我们在mazhe-maven-mall项目中创建一个测试类,来验证一下测试代码是否能够成功的引用。
- MazheMavenMallTest
public class MazheMavenMallTest {
public static void main(String[] args) {
System.out.println("Hello World Mazhe Maven Mall Test!");
MazheMavenOrder.main(args);
MazheMavenUser.main(args);
}
}
我们看测试的代码也显示出无法引用MazheMavenUser类,这也间接证明了,当第一依赖范围为compile
,第二依赖范围为test
时候,Maven不会进行传递性依赖。
compile | test | |
---|---|---|
compile | compile | - |
compile&provided
下面我们同样的验证一下,当然第一依赖范围为compile
,第二依赖范围为provided
的时候,传递性依赖的范围。我们同样的将mazhe-maven-order项目中的依赖范围修改为provided
。
- 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">
<parent>
<artifactId>midai-mazhe-maven</artifactId>
<groupId>cn.ma-zhe</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>mazhe-maven-order</artifactId>
<dependencies>
<dependency>
<groupId>cn.ma-zhe</groupId>
<artifactId>mazhe-maven-user</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
我们同样的在看一下MazheMavenMall类的代码是否能成功的引入MazheMavenUser类。
我们在看一下MazheMavenMallTest类的代码是否能引入。
我们看上图所示,不管是主代码中还是测试代码中,都无法引入MazheMavenUser类,这也就说明了,当第一依赖范围为compile
,第二依赖范围为provided
的时候,Maven不会进行传递性依赖。
compile | test | provided | |
---|---|---|---|
compile | compile | - | - |
compile&runtime
下面我们继续验证,当然第一依赖范围为compile
,第二依赖范围为runtime
的时候,传递性依赖的范围。我们继续的将mazhe-maven-order项目中的依赖范围修改为runtime
。
- 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">
<parent>
<artifactId>midai-mazhe-maven</artifactId>
<groupId>cn.ma-zhe</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>mazhe-maven-order</artifactId>
<dependencies>
<dependency>
<groupId>cn.ma-zhe</groupId>
<artifactId>mazhe-maven-user</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>runtime</scope>
</dependency>
</dependencies>
</project>
我们继续看MazheMavenMall类中的代码。
我们看上图所示,因为我们第二依赖范围是runtime,所以直接调用MazheMavenUser报错了。下面我们验证一下,如果项目运行时,会不会可以直接调用MazheMavenUser类。为了验证项目运行,我们需要通过反射的主式,来调用MazheMavenUser类,下面为具体的代码:
- MazheMavenMall
import java.lang.reflect.Method;
public class MazheMavenMall {
public static void main(String[] args) throws Exception {
System.out.println("Hello World Mazhe Maven Mall!");
MazheMavenOrder.main(args);
Class<?> clazz = Class.forName("MazheMavenUser");
Method method = clazz.getMethod("main", String[].class);
method.invoke(null, (Object) args);
}
}
- 日志
Hello World Mazhe Maven Mall!
Hello World Mazhe Maven Order!
Hello World Mazhe Maven User!
我们看通过Java反射的方式,我们成功的调用了MazheMavenUser的方法,这也就说明了当第一依赖范围为compile,第二依赖范围为runtime的时候。Maven的传递性依赖范围为runtime。下面我们验证一下测试类的代码。
- MazheMavenMallTest
public class MazheMavenMallTest {
public static void main(String[] args) {
System.out.println("Hello World Mazhe Maven Mall Test!");
MazheMavenOrder.main(args);
MazheMavenUser.main(args);
}
}
我们看上图所示,测试类中的代码可以直接调用MazheMavenUser类的方法。这是因为runtime依赖范围是对测试和运行同样有效的,所以不会报错,我们也修改一下测试类的代码,来验证一下,测试类中运行是否有问题。
- MazheMavenMallTest
import java.lang.reflect.Method;
public class MazheMavenMallTest {
public static void main(String[] args) throws Exception {
System.out.println("Hello World Mazhe Maven Mall Test!");
MazheMavenOrder.main(args);
MazheMavenUser.main(args);
Class<?> clazz = Class.forName("MazheMavenUser");
Method method = clazz.getMethod("main", String[].class);
method.invoke(null, (Object) args);
}
}
- 日志
Hello World Mazhe Maven Mall Test! Hello World Mazhe Maven Order! Hello World Mazhe Maven User! Hello World Mazhe Maven User!
这就证明了当第一依赖范围为compile,第二依赖范围为runtime的时候。Maven的传递性依赖范围为runtime。
compile | test | provided | runtime | |
---|---|---|---|---|
compile | compile | - | - | runtime |
其它依赖范围的测试,由于和上面内容类似,我们就不依依验证了,我们只要参考上面的表格就可以知道传递性依赖的范围了。所以通过上面的表格我们可以发现这样的规律:
- 当第二直接依赖的范围是compile的时候,传递性依赖的范围与第一直接依赖的范围一致。
- 当第二直接依赖的范围是test的时候,Maven不会进行传递性依赖。
- 当第二直接依赖的范围是provided的时候,只传递第一直接依赖范围也为provided的依赖,且传递性依赖的范围同样为provided。
- 当第二直接依赖的范围是runtime的时候,传递性依赖的范围与第一直接依赖的范围一致,但compile例外,此时传递性依赖的范围为runtime。