Maven Dependency Scopes
- 参考文章:https://www.baeldung.com/maven-dependency-scopes,https://blog.csdn.net/kimylrong/article/details/50353161
Maven有6个依赖域。他们分别是Compile,Provided,Runtime,Test,System,Import。这里需要解释一下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。
- Compile
默认就是compile,什么都不配置也就是意味着compile。compile表示被依赖项目需要参与当前项目的编译,当然后续的测试,运行周期也参与其中,是一个比较强的依赖。打包的时候通常需要包含进去。具有传递依赖性,也就是父依赖的scope为compile,本项目中构建的所有流程中都会包括该依赖。
- Provided
provided意味着打包的时候可以不用包进去,别的设施(Web Container)会提供。事实上该依赖理论上可以参与编译,测试,运行等周期。相当于compile,但是在打包阶段做了exclude的动作。一个典型的例子,web服务器一般在运行时提供了Servlet API。不具有传递依赖性,也就是父依赖的scope为provided,本项目中直接丢掉该依赖。
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
- Runtime
runntime表示被依赖项目无需参与项目的编译,不过后期的测试和运行周期需要其参与。与compile相比,跳过编译而已,说实话在终端的项目(非开源,企业内部系统)中,和compile区别不是很大。比较常见的如JSR×××的实现,对应的API jar是compile的,具体实现是runtime的,compile只需要知道接口就足够了。oracle jdbc驱动jar包就是一个很好的例子。另外runntime的依赖通常和optional搭配使用,optional为true。我可以用A实现,也可以用B实现。
# JDBC Driver就是一个很好的例子
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.6</version>
<scope>runtime</scope>
</dependency>
- Test
scope为test表示依赖项目仅仅参与测试相关的工作,包括测试代码的编译,执行。
# JUnit就是一个很好的例子
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
- System
从参与度来说,与provided相同,不过被依赖项不会从maven仓库抓,而是从本地文件系统拿,一定需要配合systemPath属性使用。
之前有做个一个图片视频的采集服务,项目是用maven管理的,并用docker部署。需要用到一个外部第三方包。所以我们需要把该本地包使用maven管理起来
<dependency>
<groupId>thermogroup</groupId>
<artifactId>infrared</artifactId>
<version>1.0</version>
<scope>system</scope>
<systemPath>${project.basedir}/lib/thermogroupsdk4java.jar</systemPath>
</dependency>
但是这里有一个问题,因为scope是system,所以使用maven打包的时候是不会把该本地jar包打包进去的。所以在打包docker的时候需要在dockerfile文件中显式的加进去。当时不想这么操作,通过配置maven插件的includeSystemScope参数为true,把本地包也打包进去。
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<includeSystemScope>true</includeSystemScope>
</configuration>
</plugin>
- Import
scope为import标识这个依赖会被pom文件中的所有声明的依赖替代。Import只能结合dependencyManagement使用,用来做包管理和版本控制。最常见的案例是我们使用springboot,spring cloud的时候,我们使用dependencyManagement做一个大版本管理,后续子项目可以直接通过dependency声明使用的依赖而不需要指定版本(继承dependencyManagement pom文件中声明的依赖版本)。
<dependencyManagement>
<dependencies>
<!--支持Spring Boot 2.1.X-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.10.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.1.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Maven的Dependency机制
参考文档:https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html
Maven依赖的管理是maven的核心功能,管理单个项目模块通常是简单的,但是对于复杂的项目通常涉及到上百个模块,maven的依赖管理就显得尤其重要。
Transitive Dependencies-依赖的传递性
由于依赖具有传递性,对于一个大一点的项目依赖的拓扑图就会变得越来越大,maven提供了以下几种方式来限制最终选择哪个依赖。
- Dependency mediation-依赖调节
当一个项目通过传递性依赖同一个库的不同版本时,会遵循就近原则。比如下面的这个依赖拓扑图,由于D2.0版本的库路径长度(A->B->C->D2.0)大于(A->E->D1.0)的路径长度,所以maven最终会依赖D1.0版本的库。
A
├── B
│ └── C
│ └── D 2.0
└── E
└── D 1.0
如果你需要显式的依赖D2.0,最佳实践是在自己的项目重声明依赖D2.0,这种它的路径就是(A->D2.0),会优先选择。从maven2.0.9开始,当路径长度一样时,第一声明者优先;通常我们最好显式的声明自己项目中使用的依赖,不要依靠依赖的传递性。
A
├── B
│ └── C
│ └── D 2.0
├── E
│ └── D 1.0
│
└── D 2.0
- Dependency management
参考上述对scope import的解读 - Dependency scope
参考上述对scope传递性的解读 - Excluded dependencies
显示的排除依赖
<dependency>
<groupId>group-a</groupId>
<artifactId>artifact-a</artifactId>
<version>1.0</version>
<exclusions>
<exclusion>
<groupId>group-c</groupId>
<artifactId>excluded-artifact</artifactId>
</exclusion>
</exclusions>
</dependency>
- Optional dependencies
可选依赖
<!-- declare the dependency to be set as optional -->
<dependency>
<groupId>sample.ProjectA</groupId>
<artifactId>Project-A</artifactId>
<version>1.0</version>
<scope>compile</scope>
<optional>true</optional> <!-- value will be true or false only -->
</dependency>
- 查看当前项目的已解析依赖
# 平铺
mvn dependency:list
# 层级
mvn dependency:tree
# 依赖分析,Used undeclared dependencies found:项目中使用到了,但是没有声明的依赖;Unused declared dependencies found:未使用到的依赖
mvn dependency:analyze
Maven的Profile的使用
在pom文件中声明profiles,可以设置默认profile.启动的时候以启动的分支为准。
<profiles>
<profile>
<id>local</id>
<properties>
<env>local</env>
</properties>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<profile>
<id>test</id>
<properties>
<env>test</env>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<env>prod</env>
</properties>
</profile>
</profiles>
我们可以在pom文件中通过${env}环境变量可以获取具体哪个分支,比如下面我想让不同的分支打包到harbor不同的镜像仓库,可以使用该环境变量。
<properties>
<!--docker插件-->
<!-- docker私服地址 -->
<docker.repostory>dev2.out:8082</docker.repostory>
<!--项目名,需要和Harbor中的项目名称保持一致 -->
<docker.registry.name>leather.${env}</docker.registry.name>
</properties>