基本概念
当 A jar 包需要用到 B jar 包中的类时,我们就说 A 对 B 有依赖。例如:commons-fileupload-1.3.jar 依赖于 commons-io-2.0.1.jar
通过第二个 Maven 工程我们已经看到,当前工程会到本地仓库中根据坐标查找它所依赖的 jar 包。配置的基本形式是使用 dependency 标签指定目标 jar 包的坐标。例如:
<dependencies>
<dependency>
<!—坐标 -->
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<!-- 依赖的范围 -->
<scope>test</scope>
</dependency>
</dependencies>
直接依赖和间接依赖
如果 A 依赖 B,B 依赖 C,那么 A→B 和 B→C 都是直接依赖,而 A→C 是间接依赖。
依赖的范围
当一个 Maven 工程添加了对某个 jar 包的依赖后,这个被依赖的 jar 包可以对应下面几个可选的范围:
- compile
- [1] main 目录下的 Java 代码可以访问这个范围的依赖
- [2] test 目录下的 Java 代码可以访问这个范围的依赖
- [3] 部署到 Tomcat 服务器上运行时要放在 WEB-INF 的 lib 目录下
- 例如:对 Hello 的依赖。主程序、测试程序和服务器运行时都需要用到。
- test
- [1] main 目录下的 Java 代码不能访问这个范围的依赖
- [2] test 目录下的 Java 代码可以访问这个范围的依赖
- [3] 部署到 Tomcat 服务器上运行时不会放在 WEB-INF 的 lib 目录下例如:对 junit 的依赖。仅仅是测试程序部分需要。
- provided
- [1] main 目录下的 Java 代码可以访问这个范围的依赖
- [2] test 目录下的 Java 代码可以访问这个范围的依赖
- [3] 部署到 Tomcat 服务器上运行时不会放在 WEB-INF 的 lib 目录下
- 例如:servlet-api 在服务器上运行时,Servlet 容器会提供相关 API,所以部署的时候不需要。
- runtime[了解]
- [1] main 目录下的 Java 代码不能访问这个范围的依赖
- [2] test 目录下的 Java 代码可以访问这个范围的依赖
- [3] 部署到 Tomcat 服务器上运行时会放在 WEB-INF 的 lib 目录下
- 例如:JDBC 驱动。只有在测试运行和在服务器运行的时候才决定使用什么样的数据库连接。
- 其他:import、system 等。
各个依赖范围的作用可以概括为下图:
依赖的传递性
当存在间接依赖的情况时,主工程对间接依赖的 jar 可以访问吗?这要看间接依赖的 jar 包引入时的依赖范围——只有依赖范围为 compile 时可以访问。
例如
依赖的原则
解决 jar 包冲突
- 路径最短者优先
- 路径相同时先声明者优先
这里“声明”的先后顺序指的是 dependency 标签配置的先后顺序
依赖的排除
有的时候为了确保程序正确可以将有可能重复的间接依赖排除。请看如下的例子:
● 假设当前工程为 survey_public,直接依赖 survey_environment。
● survey_environment 依赖 commons-logging 的 1.1.1 对于 survey_public 来说是间接依赖。
● 当前工程 survey_public 直接依赖 commons-logging 的 1.1.2
● 加入 exclusions 配置后可以在依赖 survey_environment 的时候排除版本为 1.1.1 的 commons-logging的间接依赖
<dependency>
<groupId>com.zhang.maven</groupId>
<artifactId>Survey160225_4_Environment</artifactId>
<version>0.0.1-SNAPSHOT</version>
<!-- 依赖排除 -->
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.2</version>
</dependency>
统一管理目标
以对 Spring 的 jar 包依赖为例:Spring 的每一个版本中都包含 spring-core、spring-context 等 jar 包。我们应该导入版本一致的Spring jar 包,而不是使用 4.0.0 的spring-core 的同时使用4.1.1 的spring-context
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
问题是如果我们想要将这些 jar 包的版本统一升级为 4.1.1,是不是要手动一个个修改呢?显然,我们有统一配置的方式:
<properties>
<spring.version>4.1.1.RELEASE</spring.version>
</properties>
……
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
这样一来,进行版本调整的时候只改一改地方就行了。