Maven依赖处理

Maven Dependency Scopes

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>
依赖范围与classpath的关系.png

依赖性传递和依赖范围.png

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>
可选依赖.png
  • 查看当前项目的已解析依赖
# 平铺
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>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,546评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,224评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,911评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,737评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,753评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,598评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,338评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,249评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,696评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,888评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,013评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,731评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,348评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,929评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,048评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,203评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,960评论 2 355

推荐阅读更多精彩内容