文章目录

  • 一、Maven optional 关键字透彻图解:
  • 1.前言:
  • 2.optional 关键字的奥秘:
  • 3.实际案例
  • 4. 反向应用
  • 二、MAVEN 中依赖作用范围概述
  • 1. Scopse 各种取值详解:
  • 2. Scope 依赖传递


一、Maven optional 关键字透彻图解:

1.前言:

在 Maven pom.xml 中,你经常会看到依赖项中有类似下面的代码:

<dependency>
  <groupId>sample.ProjectA</groupId>
  <artifactId>Project-A</artifactId>
  <version>1.0</version>
  <scope>compile</scope>
  <optional>true</optional> 
</dependency>

这里的 true 是什么意思呢?

2.optional 关键字的奥秘:

maven的provide与compile maven optional provided_spring


由于 project C 使用到了两个来自 project A 的类 (OptionalFeatureAClass) 和 project B 的类 (OptionalFeatureBClass). 如果 project C 没有依赖 packageA 和 packageB,那么编译将会失败。

project D 依赖 project C,但是对于 project D 来说,类 (OptionalFeatureAClass) 和类 (OptionalFeatureBClass) 是可选的特性,所以为了让最终的 war/ejb package 不包含不必要的依赖,使用 声明当前依赖是可选的, 默认情况下也不会被其他项目继承(好比 Java 中的 final 类,不能被其他类继承一样)

如果 project D 确实需要用到 project C 中的 OptionalFeatureAClass 怎么办呢?那我们就需要在 project D 的 pom.xml 中显式的添加声明 project A 依赖,继续看下图:

maven的provide与compile maven optional provided_jar_02


Project D 需要用到 Project A 的 OptionalFeatureAClass,那么需要在 Project D 的 pom.xml 文件中显式的添加对 Project A 的依赖

到这也就很好理解为什么 Maven 为什么要设计 optional 关键字了,假设一个关于数据库持久化的项目(Project C), 为了适配更多类型的数据库持久化设计,比如 Mysql 持久化设计(Project A) 和 Oracle 持久化设计(Project B),当我们的项目(Project D) 要用的 Project C 的持久化设计,不可能既引入 mysql 驱动又引入 oracle 驱动吧,所以我们要显式的指定一个,就是这个道理了

3.实际案例

在 spring-boot-actuator pom.xml 文件中,有超过 20 个依赖是 optional

maven的provide与compile maven optional provided_maven_03


因为 Spring Boot 不可能将没必要的依赖也打包到你最终的 jar package 中,所以用到 spring boot actuator 的项目最终生成的 jar package 中不会包含这 20 多个依赖 jar,如果你要用到哪一个,显式的加入到你的项目就好了

在接下来的文章,自定义 Spring Boot Starter 也是这个策略,因为 starter 是包含特定功能为其他项目服务用的,类似本文的 Project C 的角色了,到这里你理解 optional 的奥秘了吗?

4. 反向应用

如果 Project C 引入的依赖没有加 true,Project D 又需要依赖 Project C,但只用到 Project A 的类怎么办呢?Maven 也是有解决办法的,使用 exclusion 关键字,不多说,上一段代码就懂了:

<dependencies>
    <dependency>
      <groupId>top.dayarch.demo</groupId>
      <artifactId>Project-C</artifactId>
      <exclusions>
        <exclusion>
          <groupId>top.dayarch.demo</groupId>
          <artifactId>Project-B</artifactId>
        </exclusion>
      </exclusions> 
    </dependency>
</dependencies>

二、MAVEN 中依赖作用范围概述

Maven中使用 scope 来指定当前包的依赖范围和依赖的传递性。常见的可选值有:compile, provided, runtime, test, system 等。scope 主要是用在 pom.xml 文件中的依赖定义部分,例如:

<dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-test</artifactId>
       <version>3.2.1.RELEASE</version>
       <scope>test</scope>
   </dependency>

1. Scopse 各种取值详解:

scope取值

有效范围(compile, runtime, test)

依赖传递

例子

compile

all


spring-core

runtime

runtime, test


JDBC驱动

test

test


JUnit

system

compile, test


正如上表所示,

compile :为默认的依赖有效范围。如果在定义依赖关系的时候,没有明确指定依赖有效范围的话,则默认采用该依赖有效范围。

此种依赖,在编译、运行、测试时均有效。

provided :在编译、测试时有效,但是在运行时无效。

provided意味着打包的时候可以不用包进去,别的设施(Web Container)会提供。

事实上该依赖理论上可以参与编译,测试,运行等周期。相当于compile,但是在打包阶段做了exclude的动作。

例如:servlet-api,运行项目时,容器已经提供,就不需要Maven重复地引入一遍了。

runtime :在运行、测试时有效,但是在编译代码时无效。

说实话在终端的项目(非开源,企业内部系统)中,和compile区别不是很大。比较常见的如JSR×××的实现,对应的API jar是compile的,具体实现是runtime的,compile只需要知道接口就足够了。

例如:JDBC驱动实现,项目代码编译只需要JDK提供的JDBC接口,只有在测试或运行项目时才需要实现上述接口的具体JDBC驱动。

另外runntime的依赖通常和optional搭配使用,optional为true。我可以用A实现,也可以用B实现。

test :只在测试时有效,包括测试代码的编译,执行。例如:JUnit。

PS: test表示只能在src下的test文件夹下面才可以使用,你如果在a项目中引入了这个依赖,在b项目引入了a项目作为依赖,在b项目中这个注解不会生效,因为scope为test时无法传递依赖。

system :在编译、测试时有效,但是在运行时无效。

和provided的区别是,使用system范围的依赖时必须通过systemPath元素显式地指定依赖文件的路径。由于此类依赖不是通过Maven仓库解析的,而且往往与本机系统绑定,可能造成构建的不可移植,因此应该谨慎使用。

systemPath元素可以引用环境变量。例如:

<dependency>
      <groupId>javax.sql</groupId>
      <artifactId>jdbc-stdext</artifactId>
      <version>2.0</version>
      <scope>system</scope>
      <systemPath>${java.home}/lib/rt.jar</systemPath>
  </dependency>

2. Scope 依赖传递

A–>B–>C。当前项目为A,A依赖于B,B依赖于C。知道B在A项目中的scope,那么怎么知道C在A中的scope呢?

答案是:
当C是test或者provided时,C直接被丢弃,A不依赖C; 否则A依赖C,C的scope继承于B的scope。