作者:张丰哲
链接:https://www.jianshu.com/p/20b39ab6a88c

# 前言

在如今的互联网项目开发当中,特别是Java领域,可以说Maven随处可见。Maven的仓库管理、依赖管理、继承和聚合等特性为项目的构建提供了一整套完善的解决方案,可以说如果你搞不懂Maven,那么一个多模块的项目足以让你头疼,依赖冲突就会让你不知所措,甚至搞不清楚项目是如何运行起来的.....OK,博主就曾经被Maven“伤害”过,那么该专题的目的就是:彻底搞定Maven!

# Thinking in Maven

回想一下,当你新到一家公司,安装完JDK后就会安装配置Maven(MAVEN_HOME、path),很大可能性你需要修改settings.xml文件,比如你会修改本地仓库地址路径,比如你很可能会copy一段配置到你的settings.xml中(很可能就是私服的一些配置)。接下来,你会到IDEA或者Eclipse中进行Maven插件配置,然后你就可以在工程中的pom.xml里面开始添加<dependency>标签来管理jar包,在Maven规范的目录结构下进行编写代码,最后你会通过插件的方式来进行测试、打包(jar or war)、部署、运行。

上面描述了我们对Maven的一些使用方式,下面我们进行一些思考:

Q1:本地仓库?Maven到底有哪些仓库?它们什么关系?

Maven仓库

本地仓库路径配置

你要jar包,不可能每次都要联网去下载吧,多费劲,所以本地仓库就是相当于加了一层jar包缓存,先到这里来查。如果这里查不到,那么就去私服上找,如果私服也找不到,那么去中央仓库去找,找到jar后,会把jar的信息同步到私服和本地仓库中。

私服,就是公司内部局域网的一台服务器而已,你想一下,当你的工程Project-A依赖别人的Project-B的接口,怎么做呢?没有Maven的时候,当然是copy Project-B jar到你的本地lib中引入,那么Maven的方式,很显然需要其他人把Project-B deploy到私服仓库中供你使用。因此私服中存储了本公司的内部专用的jar!不仅如此,私服还充当了中央仓库的镜像,说白了就是一个代理!

中央仓库:该仓库存储了互联网上的jar,由Maven团队来维护,地址是:http://repo1.maven.org/maven2/。

Q2:关于<dependency>的使用

依赖管理

其实这个标签揭示了jar的查找坐标:groupId、artifactId、version

一般而言,我们可以到私服上输入artifactId进行搜索,或者到http://search.maven.org/、http://mvnrepository.com/上进行查找确定坐标。

version分为开发版本(Snapshot)和发布版本(Release),那么为什么要分呢?

在实际开发中,我们经常遇到这样的场景,比如A服务依赖于B服务,A和B同时开发,B在开发中发现了BUG,修改后,将版本由1.0升级为2.0,那么A必须也跟着在POM.XML中进行版本升级。过了几天后,B又发现了问题,进行修改后升级版本发布,然后通知A进行升级...可以说这是开发过程中的版本不稳定导致了这样的问题。

Maven,已经替我们想好了解决方案,就是使用Snapshot版本,在开发过程中B发布的版本标志为Snapshot版本,A进行依赖的时候选择Snapshot版本,那么每次B发布的话,会在私服仓库中,形成带有时间戳的Snapshot版本,而A构建的时候会自动下载B最新时间戳的Snapshot版本!

Q3:既然Maven进行了依赖管理,为什么还会出现依赖冲突?处理依赖冲突的手段是?

依赖的版本?

首先来说,对于Maven而言,同一个groupId同一个artifactId下,只能使用一个version!

根据上图的依赖顺序,将使用1.2版本的jar。

现在,我们可以思考下了,比如工程中需要引入A、B,而A依赖1.0版本的C,B依赖2.0版本的C,那么问题来了,C使用的版本将由引入A、B的顺序而定?这显然不靠谱!如果A的依赖写在B的依赖后面,将意味着最后引入的是1.0版本的C,很可能在运行阶段出现类(ClassNotFoundException)、方法(NoSuchMethodError)找不到的错误(因为B使用的是高版本的C)!

这里其实涉及到了2个概念:依赖传递(transitive)、Maven的最近依赖策略。

依赖传递:如果A依赖B,B依赖C,那么引入A,意味着B和C都会被引入。

Maven的最近依赖策略:如果一个项目依赖相同的groupId、artifactId的多个版本,那么在依赖树(mvn dependency:tree)中离项目最近的那个版本将会被使用。(从这里可以看出Maven是不是有点小问题呢?能不能选择高版本的进行依赖么?据了解,Gradle就是version+策略)

现在,我们可以想想如何处理依赖冲突呢?

想法1:要使用哪个版本,我们是清楚的,那么能不能不管如何依赖传递,都可以进行版本锁定呢?

使用<dependencyManagement>  [这种主要用于子模块的版本一致性中]

想法2:在依赖传递中,能不能去掉我们不想依赖的?

使用<exclusions> [在实际中我们可以在IDEA中直接利用插件帮助我们生成]

想法3:既然是最近依赖策略,那么我们就直接使用显式依赖指定版本,那不就是最靠近项目的么?

使用<dependency>

Q4:引入依赖的最佳实践,提前发现问题!

在工程中,我们避免不了需要加一些依赖,也许加了依赖后运行时才发现存在依赖冲突在去解决,似乎有点晚!那么能不能提前发现问题呢?

如果我们新加入一个依赖的话,那么先通过mvn dependency:tree命令形成依赖树,看看我们新加入的依赖,是否存在传递依赖,传递依赖中是否和依赖树中的版本存在冲突,如果存在多个版本冲突,利用上文的方式进行解决!

Q5:Maven规范化目录结构

简单Java工程目录结构

这里需要注意2点:

第一:src/main下内容最终会打包到Jar/War中,而src/test下是测试内容,并不会打包进去。

第二:src/main/resources中的资源文件会COPY至目标目录,这是Maven的默认生命周期中的一个规定动作。(想一想,hibernate/mybatis的映射XML需要放入resources下,而不能在放在其他地方了)

Q6:Maven的生命周期

Maven生命周期

我们只需要注意一点:执行后面的命令时,前面的命令自动得到执行

实际上,我们最常用的就是这么几个:

clean:有问题,多清理!

package:打成Jar or War包,会自动进行clean+compile

install:将本地工程Jar上传到本地仓库

deploy:上传到私服

Q7:关于scope依赖范围

既然,Maven的生命周期存在编译、测试、运行这些过程,那么显然有些依赖只用于测试,比如junit;有些依赖编译用不到,只有运行的时候才能用到,比如mysql的驱动包在编译期就用不到(编译期用的是JDBC接口),而是在运行时用到的;还有些依赖,编译期要用到,而运行期不需要提供,因为有些容器已经提供了,比如servlet-api在tomcat中已经提供了,我们只需要的是编译期提供而已。

总结来说:

compile:默认的scope,运行期有效,需要打入包中。

provided:编译期有效,运行期不需要提供,不会打入包中。

runtime:编译不需要,在运行期有效,需要导入包中。(接口与实现分离)

test:测试需要,不会打入包中。

system:非本地仓库引入、存在系统的某个路径下的jar。(一般不使用)

猜你喜欢

1、GitHub 标星 3.2w!史上最全技术人员面试手册!FackBoo发起和总结

2、如何才能成为优秀的架构师?

3、从零开始搭建创业公司后台技术栈

4、程序员一般可以从什么平台接私活?

5、37岁程序员被裁,120天没找到工作,无奈去小公司,结果懵了...

6、滴滴业务中台构建实践,首次曝光

7、不认命,从10年流水线工人,到谷歌上班的程序媛,一位湖南妹子的励志故事

8、15张图看懂瞎忙和高效的区别!

Maven是个什么鬼?,没办法起床排bug...相关推荐

  1. Maven打包pom里面配置exclude 排除掉环境相关的配置文件

    Maven打包pom里面配置exclude 排除掉环境相关的配置文件, resources excludes 关于Maven resource配制中include与exclude的关系 <inc ...

  2. 动手解决Maven在offline模式中无法使用的BUG

    本文主要解决以下问题: 1. 使用到tycho,且tycho的版本低于0.25.0 Tycho大大简化了Eclipse.OSGi插件中的pom.xml,它实际上是一系列专用于build Eclipse ...

  3. 笔记本外接显示器掉帧排bug过程

    前几天我的笔记本电脑外接显示器突然间掉帧了,卡的难受,经过我的观察,有时候掉有时候不掉,有时候笔记本自带屏不掉,有时候也会掉,这就非常amazing 跟普通人反应一样,以为是接触不良的问题,插拔了两次 ...

  4. maven 排除配置文件打包_Maven打包pom里面配置exclude 排除掉环境相关的配置文件...

    Maven打包pom里面配置exclude 排除掉环境相关的配置文件 有几种方式: 1. 打包时,指定环境参数把环境的配置文件复制过去 2. 不打包所有的环境相关的配置文件,直接由运维的人维护 可以在 ...

  5. spark需要maven管理吗_Spark-Maven全新安装:如何同时编译Java和Scala类

    我有一个Spark项目,其中同时包含.scala和.java文件.我正在尝试通过Maven构建来编译这两种类型的类.但是,当我运行" mvn clean install"时,它只会 ...

  6. maven java 参数_将Maven参数注入Java类

    我想将settings.xml配置文件参数注入Java类.我尝试使用maven-annotation-plugin,但值为null.我想知道这是不是因为这个插件是为Mojo设计的 Setting.xm ...

  7. Maven学习总结(36)——Apache Maven 3.5.0抢鲜看

    Apache家族一直备受程序员的关注,继Apache Maven 3.3.9之后,直接跳到3.5.0,至于3.4.0,程序员怕是看不到了,不过也没什么关系,功能够强大就足以!来看看是哪些新功能足以让3 ...

  8. maven 排除某个类_java-如何从Maven依赖项中排除某些程序包(在JAR中)?

    我依赖于我的Maven项目中的一个库,并且发现该库的JAR包含一些导致我的应用程序失败的软件包.我相信,如果我可以以某种方式排除这些软件包,则该库仍然可以工作,但是我想通过Maven来实现,而不是自己 ...

  9. android simple-xml,使用Maven构建Android项目-dexer在simple-xml依赖项上失败

    我有绝对空的Android项目生成 mvn archetype:generate -DarchetypeArtifactId=android-quickstart ....... 它构建良好,部署合理 ...

  10. 【bug】掘金md文本解析器bug

    bug描述: 在文章中输入(非" ``` 下输入")以下内容,则之后的内容无法显示 <style> 复制代码 bug截图 下面开始表演 这是什么鬼其妙的bug 去掉bu ...

最新文章

  1. Bootstrap3.1开发的响应式个人简历模板
  2. 疫情可以来,但比赛不能断
  3. 《2020雇佣关系趋势报告》今发布:近三成受访者兼职,近七成工作量增加、考核变严格
  4. 文计笔记 3: 软件系统
  5. JavaScript系列文章:谈谈let和const
  6. 切换 uniapp_万能前端框架uni app初探03:底部导航开发
  7. canal数据同步(canal安装)
  8. c# gdi设置画刷透明
  9. Lucene 全文搜索解析
  10. 金明的预算方案(分组背包)
  11. android studio配置国内,Android studio配置国内镜像源
  12. 数字电子技术反应时间测试电路(纯电路实现)
  13. 带本信息论看《三体》——信息论课程论文
  14. C++使用Socks5协议进行代理上网(四)
  15. 查看pagesize和blocksize以及inodesize
  16. python调用m文件
  17. Python-实现九宫格
  18. python选股模型 均线_python量化 双均线策略(金叉死叉)
  19. ►崔凯在转基因食品360度论坛上演讲
  20. Android手机视频监控系统

热门文章

  1. webpack vs gulp 一张图说明
  2. (转)常见LCD接口
  3. 人人都是 DBA(IV)SQL Server 内存管理 (转)
  4. php中array_flip数组翻转
  5. 动态指定DataGrid中多个参数的超链接列(downmoon)
  6. 「代码随想录」96.不同的二叉搜索树【动态规划】详解!
  7. 原创 leetcode[454]四数相加II /4Sum II 哈希策略
  8. 一串数字中有两个只出现一次的数字其余都是成对相同,求这两个数
  9. Illustrator 教程,了解 AI 中的绘图工具
  10. App Cleaner Uninstaller卸载清理工具 for mac