构建、依赖管理
# 00.构建、依赖管理
构建,在项目中是一个重要的概念
# 构建
在介绍和学习构建工具之前,读者应该有基本的 JavaSE 知识、了解 XML;如果你已经接触过一些软件构建和部署过程,那么对于了解构建会有一定的帮助。
任何工具都是为了解决某个痛点而创造出来的,而构建工具主要是为了解决开发软件过程中的构建问题。那么什么是构建呢?
其实,构建(也叫 build)是几乎每一位程序员都在做的工作。
我们日常开发的过程为:
- 根据需求编写代码
- 世界上没有不存在 bug 的代码,为了减少 bug,写完了代码,还要写一些单元测试,然后一个个的运行来检验代码质量,并自动生成测试文档
- 编译代码
- 运行代码,对实际功能进行测试
- 打包和部署到服务器上:再优雅的代码也是要打包的,我们需要把代码与各种配置文件、资源整合到一起,定型打包,如果是 web 项目,还需要将之发布到服务器。
- 重启项目
其中除了写代码,其他的步骤基本都是固定的、繁琐的,甚至可以说无意义的重复,完全可以交给机器来做,第 2~5 步骤,我们可以称为构建。为了自动和简化上面的工作,市面上出现了很多的构建工具,例如 Make,Ant,Maven 和 Gradle 等,使得软件的构建可以像全自动流水一样,只需要一条简单的命令,就会自动完成所有的构建。
# 依赖管理
什么是框架:框架就是别人写好的第三方工具项目,例如 Log4j,就是 Apache 公司提供的一个项目
框架与 jar 包的关系:一个框架可能有多个 jar 包,这取决于框架的规模。如果一个框架功能非常多,全都引入到一个 jar 包会使得 jar 包非常大,这是不合理的;因为我们可能只用到一小部分功能。合理的方式是一个大的框架根据模块分成几个 jar 包,用户按需引入。
依赖:如果某个项目使用了框架,我们就称该项目依赖于该框架。例如我们自己写的代码,用到了 Log4j,那么我们的项目就依赖于 Log4j。
在这个开源的年代里,几乎任何 Java 应用都会借用一些第三方的开源类库,例如日志框架 Log4j,测试框架 Junit,JDBC 等工具类,这些类库都可通过依赖的方式引入到项目中来,在编译和使用的时候,在 classpath 里指定这些 jar 包的路径即可。
但随着依赖的增多,版本不一致、版本冲突、依赖臃肿等问题都会接踵而来。举个例子,我们需要用很多个框架,用到很多个 jar 包,我们可以这样做:
- 找到各个框架的官网上(先不说找官网本身要花多少时间)
- 由于各个官网的风格不同,还得找下载链接(有时候可能还要登录才能下载,例如去 Oracle 官网下载 Java)
- 为了方便,统一将所有框架的 jar 包,拷到某个目录下,在大型的项目中,可能多达上百个 jar 包,体积巨大,造成依赖臃肿
- 有时候还有版本问题,例如 Java 的 SpringBoot 框架,在出第 3 版本的时候,不支持 Java8
- 还有依赖问题:某个框架 A,依赖另一个框架 B,而框架 C 也依赖框架 B,而框架 A 需要版本为 1.0 的框架 B,框架 C 需要 2.0 的框架 B…………这可能会造成冲突
- 如果我们要更新某个框架的版本(例如 Log4j 曾暴露出一个高危安全漏洞),又要去官网下载,然后替换一次……
如果要我们手工管理这些依赖,需要花费大量的时间。笔者曾接触过一个 2010 年开发的项目,用的就是上述管理方式……深刻体会到了依赖管理是一件非常麻烦的事情,比如在本地开发的时候,都不知道哪些 jar 包是要用到的,哪些是没用到的,如果少了就得找是哪个 jar 包。
因此,为了解决这个问题,目前几乎每个编程语言的都有自己的依赖管理工具,例如 Java 的 Maven,C/C++的 vcpkg,C#
的 NuGet,Python 的 pip,JS 的 Node……这里就不一一列举了。
另外,不少构建工具都提供了依赖管理的功能,使得依赖管理有秩序,解决了复杂的依赖管理问题,Java 的 Maven 就是除了用于构建项目以外,也有提供依赖管理的功能。
# 依赖的分类
我们可以将依赖分为几类:
二方库:自己所在的公司内部的其他项目提供的依赖
三方库:非自己所在的公司提供的依赖。
举个例子,我们使用的 Log4j 就是第三方库,类似的还有 Junit 等;
笔者曾负责的系统中,使用了短信服务。这个短信服务是公司内的另一个项目提供的(因为可能公司内很多项目都会用到短信,因此单独作为一个项目)。发送短信只需引入短信项目提供的 jar 包,调用提供的方法即可。
# 约定优于配置
约定优于配置(convention over configuration),也称作 按约定编程,是一种 软件设计范式,旨在减少软件开发人员需做出决定的数量,活得简单的好处,而又不失灵活性。
举个例子:我们使用 Log4j,都会使用配置文件,指定配置文件有两种:
一种就是不指定配置文件,Log4j 会默认寻找 Log4j 开头的配置文件;
如果你不喜欢 Log4j 默认的配置文件名,想要自己创建一个 fuckme.xml,那么就得指定配置文件,额外添加一个配置,告诉 Log4j 使用什么配置文件:
java -cp ".;log4j-jcl-2.19.0.jar;log4j-api-2.19.0.jar;log4j-core-2.19.0.jar" -Dlog4j.configurationFile="fuckme.xml" HelloLog4j
1
使用第二种方式,其实是不推荐的。首先你得额外加配置,告诉 Log4j 配置文件是哪个;另外别人看到你这个文件名 fuckme.xml,是不知道这个文件是做啥用的。如果使用的框架比较多,那么配置文件也会比较多,并且全都自己指定的话,那么就要加很多配置,另外别人看到都不知道这些文件的作用(可能自己过一段时间后都不知道了,跟别说接手别人的项目的时候)
而如果使用第一种方式,就可以减少很多的配置,并且别人根据文件名就可以知道这个文件是做什么的:例如 Log4j.xml,别人一看就知道是 Log4j 框架的配置文件;web.xml 就是 web 项目的配置文件。如果每个项目都使用默认配置,那么就很利于维护,从一个项目切换到另一个项目就不会那么难,因为大家都知道默认配置是怎样的,如果要修改某个配置文件要怎么做。
第一种方式,就是一种约定大于配置的方式,相当于框架的作者和开发者约定好:我们的默认配置是这个,你不配置就会使用默认配置。这就是标准的好处。
对于构建,约定大于配置的设计也非常有用。在 Maven 之前,十个项目可能有十种构建方式;有了 Maven 之后,所有项目的构建命令都是简单的,一致的,这极大地避免了不必要的学习成本,而且有利于促进项目团队的标准化。
- 01
- 中国网络防火长城简史 转载10-12
- 03
- 公告:博客近期 RSS 相关问题10-02