天天使用Maven,你是否审视过它的约定优于配置
前言
基于Maven来构建、管理我们的开发项目,初、中、高级的后端研发,甚至是架构师都在使用,日常频繁使用,恰恰又是大家容易忽略的。今天我们再来回顾一下,聊聊为什么Maven在我们日常开发的项目中这样流行。
不知道大家还记得下面这本畅销书吗,很厚的一本,包含了很多内容,今天我们主要从研发管理与规范的角度,在实际开发中常用到的点来写。

不是只有Maven
大家肯定也都听过或者用过Gradle:
Gradle和Maven是Java世界中两个重要的自动化构建工具。
Gradle于2007年首次发布,并于2013年被Google用作Android项目的构建系统,是开源项目。
Maven是apache旗下的一个开源项目,是一款用于管理和构建java项目的工具,第一个版本于2004年发布。
他俩有各自的特点
维度 | Maven | Gradle |
---|---|---|
构建逻辑 | 使用一个固定的生命周期,每个阶段都有明确的任务。这种结构对于初学者来说更容易理解,但可能不够灵活。 | 提供了基于有向无环图(DAG)的构建逻辑,允许定义任务之间的依赖关系。这使得Gradle更加灵活,适合处理复杂的构建需求。 |
项目模型 | 基于POM(Project Object Model)的概念,每个项目都有一个中央POM文件定义项目的元数据和依赖关系 | 使用基于域特定语言(DSL)的构建脚本,通常基于Groovy语言。这使得Gradle脚本更加直观和易读 |
依赖管理 | 有一个完善的本地仓库和远程仓库的路线设计,管理依赖非常方便 | 也可以使用Maven仓库,但同时提供了更灵活的依赖管理机制,例如使用“配置”来管理依赖项 |
插件系统 | 拥有庞大的插件生态系统,使得为项目添加新功能变得容易 | 也支持插件,但其插件系统与Maven有所不同,Gradle的插件可以与主构建脚本一起使用Groovy或Kotlin编写 |
多项目构建 | 支持多模块构建,可以很容易地在一个父POM中管理多个子项目的依赖和配置 | 同样支持多项目构建,并且提供了更灵活的构建脚本结构来处理复杂的项目布局 |
配置与声明性语法 | 主要使用XML进行配置,虽然XML在一些人看来可能有点冗长和繁琐,但它具有清晰的语法结构 | 使用基于Groovy的DSL进行配置,这使得配置更加简洁和易读。然而,这也意味着需要额外学习DSL的语法 |
社区和支持 | 由于其悠久的历史和广泛的应用,Maven拥有庞大的用户基础和丰富的文档。许多企业项目都选择使用Maven | 虽然起步较晚,但随着时间的推移,Gradle的用户基础也在不断增长。它被广泛用于Android和其他非Java项目的构建中。由于其灵活性,许多开发者和团队更倾向于选择Gradle |
升级与维护 | 由于其固定的生命周期和清晰的升级路径,升级Maven项目通常比较简单 | 由于其高度可配置的性质,升级可能稍微复杂一些,但Gradle提供了很好的向后兼容性 |
移动应用支持 | 主要用于传统的Java应用程序开发,尽管也有一些移动应用的例子,但并不是其主要领域 | 特别适用于Android应用程序的开发,许多流行的Android开发工具和插件都与Gradle集成 |
集成与扩展性 | 主要专注于Java项目,尽管它也可以与其他语言的项目集成,但这通常需要额外的配置和插件 | 由于其灵活性和可扩展性,可以轻松地与各种类型的项目集成,包括非Java项目。这使得Gradle在需要混合技术的项目中更具优势。 |
重温Maven的定义
按照官网的定义:它是一个的软件项目管理工具。它基于POM(project object model,项目对象模型),可以通过中央化的信息(central piece of information)实现对项目的构建和管理工作。
这里梳理了下其中的关键词,,也是本篇文章的重点:
关键词:项目管理工具;基于****POM ;中央化的信息;
优点:高效协作,规范、标准化,跨平台;
Maven的作用
- 依赖管理:管理项目依赖资源(jar包)、避免资源版本冲突(版本冲突控制);
- 项目构建:提供标准、跨平台、自动化的项目构建方式;完成项目代码的编译、测试、打包和部署;
- 统一开发结构:提供标准的、统一的项目结构;
依赖管理
添加第三方 jar 包 | |
---|---|
在今天的 JavaEE 开发领域,有大量的第三方框架和工具可以供我们使用。要使用这些 jar 包最简单的方法就是复制粘贴到 WEB-INF/lib 目录下。但是这会导致每次创建一个新的工程就需要将 jar 包重复复制到 lib 目录下,从而造成工作区中存在大量重复的文件,让我们的工程显得很臃肿。 | 而使用 Maven 后每个 jar 包本身只在本地仓库中保存一份,需要 jar 包的工程只需要以坐标的方式简单的引用一下就可以了。不仅极大的节约了存储空间,让项目更轻巧,更避免了重复文件太多而造成的混乱。 |
获取第三方 jar 包 | |
---|---|
JavaEE 开发中需要使用到的 jar 包种类繁多,几乎每个 jar 包在其本身的官网上的获取方式都不尽相同。为了查找一个 jar 包找遍互联网,身心俱疲,没有经历过的人或许体会不到这种折磨。不仅如此,费尽心血找的 jar 包里有的时候并没有你需要的那个类,又或者又同名的类没有你要的方法——以不规范的方式获取的 jar 包也往往是不规范的。 | 使用 Maven 我们可以享受到一个完全统一规范的 jar 包管理体系。你只需要在你的项目中以坐标的方式依赖一个 jar 包,Maven 就会自动从中央仓库进行下载,并同时下载这个 jar 包所依赖的其他 jar 包。 |
jar 包之间的依赖关系 | |
---|---|
jar 包往往不是孤立存在的,很多 jar 包都需要在其他 jar 包的支持下才能够正常工作,我们称之为jar 包之间的依赖关系。最典型的例子是:commons-fileupload-1.3.jar 依赖于 commons-io-2.0.1.jar,如果没有 IO 包,FileUpload 包就不能正常工作。 | |
那么问题来了,你知道你所使用的所有 jar 包的依赖关系吗?当你拿到一个新的从未使用过的 jar 包,你如何得知他需要哪些 jar 包的支持呢?如果不了解这个情况,导入的 jar 包不够,那么现有的程序将不能正常工作。再进一步,当你的项目中需要用到上百个 jar 包时,你还会人为的,手工的逐一确认它们依赖的其他 jar 包吗?这简直是不可想象的。 | 而引入 Maven 后,Maven 就可以替我们自动的将当前 jar 包所依赖的其他所有 jar 包全部导入进来,无需人工参与,节约了我们大量的时间和精力。用实际例子来说明就是:通过 Maven 导入 commons-fileupload-1.3.jar 后,commons-io-2.0.1.jar 会被自动导入,程序员不必了解这个依赖关系。 |
公司内部jar的引用 |
---|
我们通常也会会封装一些自己的Jar,假如我们要改这个Jar包,我们还需要手动打包,再次将项目当中引用的Jar包给替换掉,使用Maven后,一个命令即可解决。 |
jar冲突的管理
使用一个框架Jar包他可能出现多层依赖关系,有时候为了解决依赖冲突,必须要去了解他们之间的关系,了解关系相对来说是非常麻烦的,必要的时候还需要通过反编译,而使用Maven之后,直接可以生成依赖关系图。
- 依赖调解(Dependency Mediation) :Maven会选择最近的依赖版本。例如,如果项目A依赖项目B,而项目B同时依赖项目C的两个不同版本,Maven将使用路径最短的那个版本。
- 依赖排除(Dependency Exclusion) :在
pom.xml
中指定不想要的依赖版本。 - 依赖范围(Dependency Scope) :设置依赖的
scope
来限制哪些依赖在不同的构建生命周期中是可用的。例如,设置scope
为test
的依赖只在测试时可用。 - 第一级依赖优先原则(First-Declaration Wins) :在
pom.xml
中先声明的依赖版本将会被优先使用。 - Maven 解析策略:Maven会按照
pom
文件中定义的顺序解析依赖,并使用第一个找到的依赖。
tips :
基于Maven Helper的冲突排查
统一的开发结构
约定优于配置(Convention Over Configuration)是 Maven 最核心的设计理念之一 ,Maven对项目的目录结构、测试用例命名方式等内容都做了规定,凡是使用 Maven 管理的项目都必须遵守这些规则
。
降低管理、维护的复杂度,减少学习成本。
统一的版本管理
项目中会对很多第三方框架的jar包进行依赖:
同一个框架的jar包版本应该是一致的,如果希望修改框架的版本时,每一个jar包的版本都需要进行改变;
如果手动一个一个修改配置依赖,太麻烦;可以设置属性标签,定义jar包的版本,然后通过${}表达式来进行引用;
项目构建
Maven对项目的管理还包括快捷的生命周期控制。JAVA开发的生命周期包括:搭建项目结构、编码、测试、编译、打包、发布到环境测试、发布到生产环境。Maven实现了自动化的控制,通过简单的命令或者可视化的按钮,即可实现相应生命周期的控制,免去了开发者自己编写大量脚本代码执行的过程。
Maven的使用
安装与部署
这里就不再写了,网上的教程一搜一车。
仓库(中央化的信息)
本地仓库
Maven 的本地仓库,在安装 Maven 后并不会创建,它是在第一次执行 Maven 命令的时候才被创建。
运行 Maven 的时候,Maven 所需要的任何构件都是直接从本地仓库获取的。如果本地仓库没有,它会首先尝试从远程仓库下载构件至本地仓库,然后再使用本地仓库的构件。
默认情况下,不管Linux还是 Windows,每个用户在自己的用户目录下都有一个路径名为 .m2/repository/ 的仓库目录。
Maven 本地仓库默认被创建在 %USER_HOME% 目录下。要修改默认位置,在 %M2_HOME%\conf 目录中的 Maven 的 settings.xml 文件中定义另一个路径。
中央仓库
中央仓库包含了绝大多数流行的开源Java构件,以及源码、作者信息、SCM、信息、许可证信息等。一般来说,简单的Java项目依赖的构件都可以在这里下载到。
中央仓库的关键概念:
这个仓库由 Maven 社区管理。不需要配置。需要通过网络才能访问。
要浏览中央仓库的内容,Maven 社区提供了一个 URL:Maven Central Repository Search。使用这个仓库,开发人员可以搜索所有可以获取的代码库。
Maven 仓库默认在国外, 国内使用难免很慢,我们可以更换为阿里云的仓库。
远程仓库
远程仓库是指位于网络上的仓库,它可以是Maven官方仓库,也可以是由组织或个人搭建的私有仓库。远程仓库是Maven构建过程中下载依赖的主要来源,开发者可以在项目的pom.xml文件中配置远程仓库,指定Maven在构建项目时从哪些远程仓库获取依赖。
私服也是远程仓库中的一种
如果我们一个团队中有几百个人在开发一些项目,都是采用Maven的方式来组织项目,那么我们每个人都需要从远程仓库中把需要依赖的构件下载到本地仓库,这对公司的网络要求也比较高,为了节省这个宽带和加快下载速度,我们在公司内部局域网内部可以架设一台服务器,这台服务器起到一个代理的作用,公司里面的所有开发者去访问这个服务器,这台服务器将需要的构建返回给我们,如果这台服务器中也没有我们需要的构建,那么这个代理服务器会去远程仓库中查找,然后将其先下载到代理服务器中,然后再返回给开发者本地的仓库。
还有公司内部有很多项目之间会相互依赖,你可能是架构或者平台组的,你需要开发一些jar包给其他组使用,此时,我们可以将自己jar发布到私服中给其他同事使用,如果没有私服,可能需要我们手动发给别人或者上传到共享机器中,不过管理起来不是很方便。
构建多模块项目
Maven 多模块项目的优点有:
- 代码复用:多模块项目可以将公共的代码抽离出来独立的模块,在其他模块中直接引用,提高了代码的复用性。
- 模块化管理:多模块项目可以将项目按照功能或者业务进行分割,每个模块都有自己的职责和功能,可以更好地进行模块化管理,提高项目的可维护性和可扩展性。
- 并行构建:多模块项目可以并行构建,加快项目的编译和构建速度,提高开发效率。
- 版本管理:多模块项目可以更好地管理各个模块的版本依赖关系,能够方便地进行版本升级和管理。
Maven 多模块项目的缺点有:
- 配置复杂:多模块项目需要配置父子模块关系、依赖关系等,配置相对复杂,使用起来较为繁琐。
- 编译耗时:由于多模块项目需要进行并行构建,所以在编译过程中可能会消耗更多的时间和资源。
- 系统复杂性:多模块项目的系统复杂性相对较高,模块之间的依赖关系和引用关系需要谨慎管理,否则容易产生冲突和错误。
POM的配置与使用
这里也不再过多的说明,就是有一点,大家看下自己的项目中,你的jar版本有在统一管理了吗,还是任由每个开发人员自己添加。
总结
Maven之所以被广泛使用,我相信,和他的管理、中央化、高效协作属性是分不开的。这样正是我们在日常的开发管理中需要重点关注的,就像上文我们写的:“约定优于配置(Convention Over Configuration)是 Maven 最核心的设计理念之一”。相信在我们充分了解了Maven的作用和优点,在接下来每个研发同学使用时,才会从认识层面真正认识到他的重要性,并遵循其规范。
后记-脚手架的必要性
相信稍微大一点的公司,都会有自己的脚手架,并不断地迭代。一个是规范和约定整个公司的研发团队所使用的项目结构,并能降低管理、维护的复杂度,减少学习成本。
遵循复用原则、DRY原则、开闭原则、不要重新造轮子。
从我的角度,一般我会从如下几个角度做规范和约束:
- 统一的jar及版本的引入与管理
- 工具类的统一管理、维护与应用(比如Math、String、JSON、File等等各方面的)
- 包名与结构的统一约定
- 防攻击、防安全渗透的统一维护(比如xss、sql注入等)
- 子项目按业务的拆分管理
- 统一的接口请求与响应
- 日志输出规范
- 基础运维:容灾、链路追踪、日志收集等
- redis的查询与写入,key的声明规则(我见过很多,开发人员完全自己随意声明key的)
- JDBC层的约束
转载自:https://juejin.cn/post/7383990445049544743