1 问题背景
1.1 一起maven dependency scope := provided 引发的事故
今天在运行科室的bdp-common-resource
工程时,在src/main
目录下一个类中插入了一段含main
方法的临时调试代码:
import lombok.extern.slf4j.Slf4j;
// ...
@Slf4j
public class DBCFileUtils {
// ...
public static void main(String[] args) {
System.out.println("hello");
}
// ...
}
- 控制台运行结果却赫然线显示slf4j日志错误,无法正常显示本应输出的日志:
java.lang.ClassNotFoundException: org.slf4j.LoggerFactory
- 经过分析,知道:
org.slf4j.LoggerFactory
属于org.slf4j:slf4j-api
包- 直接原因:目标工程在运行编译的包,没有引入
org.slf4j:slf4j-api
这一依赖包
- 难道是没有引入
slf4j
/log4j
包吗?非也:
<!-- log dependency -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<scope>provided</scope>
</dependency>
- 几经排查,发现:问题就出在
scope:=provided
上!
将
scope
的值由provided
改为compile
,一切就解决了~
- 经过这次事件,有必要好好学习/复习一下: maven dependency 的
scope
了
2 Maven dependency scope
Maven中的scope主要有以下6种。
接下来分别介绍下这几种scope:
2.1 compile
- 不声明scope元素的情况下的默认值;
compile
表示被依赖包需要参与当前项目的编译,包括后续的测试,运行周期也参与其中,是一个比较强的依赖;- 打包阶段:依赖会被打入到项目jar包中。
2.2 provided
provided
类型的scope只会在项目的编译、测试阶段起作用;- 可以认为在目标容器中已经提供了这个依赖,无需再提供,但是在编写代码或者编译时可能会用到这个依赖;
- 打包阶段:依赖不会被打入到项目jar包中。
说到provided,这里就要说到<dependency>下的子标签<optional> :
如果一个依赖的 <optional> 设置为true,则:
该依赖在打包时,不会被打进jar包;同时,不会通过依赖传递传递到依赖该项目的工程;
例如:x依赖B,B由依赖于A(x->B->A),则A中设置<optional> 为true的依赖不会被传递到x中。
这两者的区别在于:
1、<optional>为true 表示某个依赖可选,该依赖是否使用都不会影响服务运行;
2、provided的<scope>在目标容器中已经提供了这个依赖,无需再提供。(潜台词,如果目标容器无此依赖,就可能会出错)
2.3 runtime
- runtime与compile比较相似,区别在于:
runtime
跳过了编译阶段,打包时通常需要包含进去。
2.4 test
- 在一般的编译和运行时都不需要,它们只有在测试编译阶段和测试运行阶段可用
- 不会被打包到项目jar包中;
- 同时,如果项目A依赖于项目B,项目B中的test作用域下的依赖不会被继承。
2.5 system
- 表示使用本地系统路径下的jar包,需要和一个systemPath一起使用,如下:
<!--引用-->
<dependency>
<groupId>xxxx</groupId>
<artifactId>xxx</artifactId>
<systemPath>${basedir}/lib/xxxxx.jar</systemPath>
<scope>system</scope>
<version>1.4.12</version>
</dependency>
2.6 import
import
只能在pom.xml
文件的<dependencyManagement>
中使用,从而引入其他的pom文件中引入依赖。
如:在
Spring boot
项目的POM文件中,我们可以通过在POM文件中继承Spring-boot-starter-parent
来引用Srping boot
默认依赖的jar包,如下:
<!-- Inherit defaults from Spring Boot -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.BUILD-SNAPSHOT</version>
</parent>
- 但是,通过上面的方法:parent继承的方法,只能继承一个
spring-boot-start-parent
。 - 实际开发中,用户很可能需要继承自己公司的标准
parent
配置,这个时候可以使用scope=import
来实现多继承。代码如下:
<dependencyManagement>
<dependencies>
<dependency>
<!-- Import dependency management from Spring Boot -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.0.1.BUILD-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
- 通过上面方式,就可以获取
spring-boot-dependencies.2.0.1.BUILD-SNAPSHOT.pom
文件中dependencyManagement
配置的jar包依赖。 - 如果要继承多个,可以在
dependencyManagement
中添加,如:
<dependencyManagement>
<dependencies>
<!-- Override Spring Data release train provided by Spring Boot -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-releasetrain</artifactId>
<version>Fowler-SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.0.1.BUILD-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>