跳转至

现实世界中的Spring#

约 11119 个字 8 张图片 预计阅读时间 222 分钟

本章内容包括:

  • 框架的定义
  • 何时应使用框架,何时应避免使用框架
  • Spring 框架简介
  • Spring 在实际场景中的应用

Spring 框架(简称 Spring)是 Java 生态系统中的一个应用框架。应用框架是一套通用的软件功能,为开发应用程序提供了基础结构。应用框架通过免去从零开始编写所有程序代码的繁琐工作,从而简化了应用开发的过程。

如今,我们在开发各种应用时都会用到 Spring,无论是大型后端解决方案,还是自动化测试应用。根据多份关于 Java 技术的调查报告(比如 JRebel 2020 年的这份报告:http://mng.bz/N4V7,或者 JAXEnter 的这份报告:http://mng.bz/DK9a),Spring 已经成为目前最常用的 Java 框架。

Spring 现在非常受欢迎,越来越多的开发者不仅用它配合 Java,还会和其他 JVM 语言一起使用。近几年,我们发现使用 Spring 搭配 Kotlin(JVM 家族中另一款备受青睐的语言)的开发者数量有了显著增长。在本书中,我们将重点讲解 Spring 的基础知识,并带你掌握在实际项目中使用 Spring 的核心技能。为了让你更轻松地理解内容、专注于 Spring 本身,书中所有示例都将采用 Java 语言。接下来的章节里,我们会通过实例讲解并实践一些关键技能,比如如何连接数据库实现应用间通信,以及 如何保障应用安全和进行测试

在深入探讨接下来的技术细节之前,我们先来聊聊 Spring 框架,以及它的实际应用场景。Spring 为什么如此受欢迎?又该在什么情况下使用它呢?

在本章中,我们将重点介绍什么是框架,特别是以 Spring 框架为例。在 1.1 节,我们会讨论使用框架的优势;在 1.2 节,我们将介绍 Spring 生态系统,以及入门 Spring 所需了解的各个组件。接下来,我会带你了解 Spring 框架的多种应用场景,尤其是在 1.3 节 中探讨实际案例。1.4 节则会讨论在什么情况下不适合使用框架。在你尝试使用 Spring 框架之前,必须先了解这些内容,否则就可能会像用锤子去挖花园一样,用错了工具。

根据你的水平,这一章可能会让你觉得有些难。我可能会介绍一些你以前没听说过的概念,这可能会让你感到困惑。但别担心,就算现在有些内容你还没弄明白,后面在书中都会为你详细解释。有时候,在本书的后续章节里,我会提到前面章节讲过的内容。我采用这种方式,是因为学习像 Spring 这样的框架,并不是一条线性的学习路径,有时候你需要等到掌握了更多知识点后,才能拼出完整的图景。但最终,你一定会对整体有清晰的认识,并掌握开发专业级应用所需的宝贵技能。

为什么我们要使用框架?#

在本节中,我们将探讨框架这一主题。框架到底是什么?它是如何出现的,又是出于什么原因?只有了解某样东西能为你带来哪些价值,你才会有动力去使用它。Spring 也不例外。我会结合自己的实践经验,以及在真实项目中学习和使用各种框架(包括 Spring)所积累的知识,向你讲解这些关键细节。

应用框架是一套用于构建应用程序的功能集合。它为我们提供了丰富的工具和功能,帮助开发应用。你不需要用到框架提供的所有功能,而是可以根据应用的具体需求,选择合适的部分来使用。

这里有一个我很喜欢的关于应用框架的比喻。你有没有在宜家这样的DIY商店买过家具?比如说你买了一个衣柜——你拿到的不是已经组装好的衣柜,而是组装所需的所有零件,以及一份详细的安装说明书。现在想象一下,如果你订购了一个衣柜,但收到的不是只够组装衣柜的零件,而是所有可以用来组装各种家具的零件:比如桌子、衣柜等等。如果你想要一个衣柜,你就得自己挑出合适的零件,然后把它们组装起来。应用框架就像这样。它为你提供了构建应用所需的各种软件组件。你需要知道该选择哪些功能,以及如何把它们组合起来,才能实现你想要的效果(见图1.1)。


图1.1 大卫在UAssemble商店订购了一个衣柜。但这家商店(框架)并不会只把大卫(程序员)需要的组件(软件功能)送到他手上来组装新衣柜(应用)。相反,商店把所有他可能用得上的零件都寄给了他。至于哪些组件(软件功能)合适、该如何组合,才能得到理想的成品(应用),完全由大卫(程序员)自己决定。

框架这一概念并不新鲜。在软件开发的历史长河中,程序员们发现,他们可以在多个应用程序中重复利用自己写过的部分代码。最初,由于应用数量有限,每个应用都是独一无二的,通常会用特定的编程语言从零开始开发。随着软件开发领域的不断扩展,市场上涌现出越来越多的应用,人们也更容易发现,这些应用之间往往有许多 相似的需求。我们来举几个例子:

  • 在每个应用程序中,都会记录错误、警告和信息日志。
  • 大多数应用程序在处理数据变更时都会使用事务。事务是一种非常重要的机制,用于确保数据的一致性。我们将在第13章对此进行详细讨论。
  • 大多数应用程序都会采用防护机制来应对常见的安全漏洞。
  • 大多数应用程序之间的通信方式都很相似。
  • 大多数应用程序都会采用类似的机制来提升性能,比如缓存或数据压缩。

而且这个清单还在继续。事实证明,应用程序中实现的业务逻辑代码其实远远少于支撑整个应用运转的各种轮子和传送带(通常也被称为 底层管道(the plumbing))的代码量。

当我提到业务逻辑代码时,我指的是实现应用程序业务需求的那部分代码。也就是说,这些代码负责实现用户对应用的各种期望。例如, 点击某个特定链接会生成一张发票就是用户期望发生的事情。你开发的应用中有一部分代码正是用来实现这个功能的,这部分代码就是开发者所说的业务逻辑代码。然而,任何应用还需要处理许多其他方面,比如 安全性日志记录数据一致性等等(见图1.2)。


图1.2 用户视角就像一座冰山。用户主要看到的是业务逻辑代码的结果,但这只是构建应用完整功能的一小部分。正如冰山的大部分都隐藏在水下,在企业级应用中,大多数代码其实并不为用户所见,因为它们由各种依赖项提供。

此外,从功能角度来看,业务逻辑代码正是让一个应用区别于另一个应用的关键。如果你拿一个网约车系统和一个社交网络应用来比较,它们的使用场景就完全不同。

Note

用例指的是用户使用应用程序的原因。例如,在一个网约车应用中,一个用例就是叫车;而在一个管理外卖的应用中,一个用例则是点披萨

你采取的操作虽然不同,但都需要数据存储数据传输日志记录安全配置,可能还包括缓存 等。这些与业务无关的功能,其实可以被不同的应用重复利用。那么,每次都要重新实现这些功能,真的高效吗?当然不是:

  • 通过复用现有的东西,而不是自己从头开发,你可以节省大量的时间和金钱。
  • 许多应用已经在使用的现有实现,出错的概率更低,因为已经有很多人帮你测试过了。
  • 你还能受益于社区的建议,因为有很多开发者都了解这套功能。如果你是自己写的代码,可能只有极少数人懂。

转型之路

我参与的第一个项目之一,是一个用Java开发的大型系统。这个系统由多个围绕着老式架构服务器设计的应用组成,所有应用都是用Java SE从零开始编写的。这个项目大约是在25年前,Java语言刚刚出现时启动的,这也是它现在这个样子的主要原因。几乎没有人能预料到它后来会变得如此庞大。那个年代,系统架构中更先进的理念还没有出现,由于网络连接速度缓慢,整体的运作方式也和现在的独立系统有很大不同。

随着时间的推移,几年后,这个应用程序变得越来越像一团乱麻。出于一些合理的原因(这里就不详细展开了),团队决定必须转向一种现代化的架构。要实现这一转变,首先需要对代码进行整理,而其中一个关键步骤就是引入框架。我们最终选择了Spring。当时,我们也考虑过Java EE(现在叫Jakarta EE),但团队大多数成员认为Spring是更好的选择,因为它更轻量,实施起来更简单,我们也觉得后期维护会更容易。

这次转型并不容易。我和几位在各自领域非常专业、对这个应用也非常了解的同事一起,投入了大量精力推动这项变革。

效果令人惊喜!我们删减了超过40%的代码行数。这次转变让我第一次真正体会到使用框架所带来的巨大影响。

Note

选择和使用框架与应用程序的设计和架构密切相关。在学习 Spring 框架的同时,深入了解这些主题会对你大有裨益。附录A中对软件架构进行了讨论,并提供了丰富的资源,方便你进一步深入学习。

Spring生态系统#

在本节中,我们将讨论 Spring 及其相关项目,比如 Spring BootSpring Data 。你将在本书中全面了解这些内容,以及它们之间的联系。在实际开发中,通常会将不同的框架结合使用,每个框架都能帮助你更高效地实现应用程序的某一部分功能。

我们通常称 Spring 为一个框架,但它实际上远比这复杂。Spring 是一个由多个框架组成的生态系统。通常,当开发者提到 Spring 框架时,他们指的是其中包含的部分软件功能,包括以下内容:

  1. Spring Core——Spring 的核心部分之一,包含了基础功能。其中一项重要特性就是 Spring 上下文(Spring Context) 。正如你将在第2章详细学习到的,Spring 上下文是 Spring 框架的核心能力,使得 Spring 能够管理你应用中的实例。此外,Spring Core 还包括了 Spring 的切面功能。切面可以帮助 Spring 拦截并操作你在应用中定义的方法。关于切面的更多细节,我们会在第6章进一步探讨。 Spring 表达式语言(SpEL)也是 Spring Core 的一部分,它允许你通过特定的语言来描述 Spring 的配置。这些概念对你来说可能还比较新,我并不指望你现在就完全理解。但很快你就会明白,Spring Core 提供了 Spring 集成到你的应用中的各种机制。
  2. Spring model-view-controller (MVC)——Spring 框架中用于开发响应 HTTP 请求的 Web 应用的部分。从第7章开始,我们将会使用 Spring MVC。
  3. Spring Data Access——同样是 Spring 的核心组成部分之一。它为你提供了连接 SQL 数据库、实现应用持久层的基础工具。从第13章开始,我们会用到 Spring 数据访问相关内容。
  4. Spring testing——包含了为你的 Spring 应用编写测试所需的各种工具。关于这一主题,我们将在第15章进行讨论。

你可以把 Spring 框架初步想象成一个太阳系,其中 Spring Core 就像是中央的恒星,将整个框架紧密地联系在一起(见图 1.3)。


图1.3 你可以把Spring框架想象成一个太阳系,Spring Core位于中心。各种软件功能就像围绕Spring Core运转的行星,被其引力牢牢吸引在周围。

探索Spring Core:Spring的基石#

Spring Core 是 Spring 框架中提供基础集成机制的核心部分。Spring 的工作原理基于控制反转(IoC)原则 。在采用这一原则时,我们不是让应用程序自身来控制执行流程,而是将控制权交给其他软件——在这里,就是 Spring 框架。通过配置,我们告诉框架如何管理我们编写的代码,这些代码定义了应用的业务逻辑。这也正是 IoC 中反转(inversion) 一词的由来:你不再让应用通过自身的代码和依赖来控制执行流程,而是让框架(作为依赖)来掌控应用及其代码的运行(见图 1.4)。

Note

在这里,控制(controls)一词指的是诸如创建实例调用方法等操作。框架可以创建你在应用中定义的类的对象。根据你编写的配置,Spring 会拦截方法,并为其增强各种功能。例如,Spring 可以拦截某个特定方法,在方法执行过程中记录可能出现的任何错误。


图 1.4 控制反转(IoC)。在 IoC 场景下,应用程序的执行权由依赖项掌控,而不是应用自身主动调用其他依赖。Spring 框架在应用运行期间负责控制应用的执行,因此它实现了控制反转的执行方式。

你将从学习 Spring Core 开始,第二到第五章会介绍 Spring 的 IoC 功能。IoC 容器将 Spring 组件和你应用程序的各个部分与框架紧密结合在一起。通过 IoC 容器(通常也称为 Spring 上下文),你可以让 Spring 识别特定的对象,从而让框架按照你的配置方式来使用它们。

在第6章中,我们将继续探讨Spring的面向切面编程(AOP)。Spring能够管理添加到其IoC容器中的实例,其中一项功能就是拦截这些实例行为所对应的方法。这种能力被称为 对方法进行切面处理。Spring AOP是框架与应用程序行为交互最常见的方式之一,因此也是Spring的核心功能之一。Spring Core的一部分还包括 资源管理,国际化(i18n)类型转换以及SpEL。我们将在本书的各个示例中陆续接触到这些功能的相关内容。

使用Spring的数据访问功能实现应用的数据持久化#

对于大多数应用来说,持久化部分处理的数据是至关重要的。与数据库打交道是开发中的基础内容,而在 Spring 框架中,数据持久化通常由 Data Access(数据访问)模块负责。Spring 的数据访问不仅支持 JDBC,还可以与对象关系映射(ORM)框架(如 Hibernate)集成(如果你还不了解 ORM 框架或没听说过 Hibernate,也不用担心,我们会在本书后续章节详细介绍这些内容),同时还负责事务管理。在第 12 到第 14 章中,我们将全面讲解如何使用 Spring Data Access,帮助你顺利入门。

Spring MVC开发Web应用的能力#

在 Spring 框架中,最常见的应用类型就是 Web 应用。Spring 生态系统为开发 Web 应用和 Web 服务提供了丰富的工具,你可以用多种方式来构建 Web 应用。Spring MVC 允许你以标准的 Servlet 方式开发应用,这也是目前绝大多数应用所采用的方式。在第 7 章,我们会更详细地介绍如何使用 Spring MVC

Spring 测试功能#

Spring 的测试模块为我们提供了一整套丰富的工具,用于编写单元测试和集成测试。关于测试的话题已经有很多资料和文章,但在第15章中,我们会重点讲解你入门 Spring 测试所需掌握的核心内容。我还会推荐一些值得阅读的资源,帮助你全面了解这个主题。我的经验法则是:如果你不懂测试,就还称不上是一名成熟的开发者,所以这个话题你一定要重视。

Spring 生态系统中的项目#

Spring 生态系统远不止前面提到的那些功能,它还包含了大量其他能够良好集成的框架,共同构建了一个庞大的生态圈。比如, Spring DataSpring SecuritySpring CloudSpring BatchSpring Boot 等等,都是其中的重要项目。在开发应用时,你可以将这些项目灵活组合使用。例如,你可以同时用到 Spring BootSpring SecuritySpring Data 来构建应用。在接下来的几章中,我们会通过一些小型项目,实际体验 Spring 生态系统中不同项目的用法。

这里所说的项目,指的是 Spring 生态系统中独立开发的组成部分。每个项目都有专门的团队负责功能扩展,并且在 Spring 官方网站上都有独立的介绍和文档,具体可以参考:https://spring.io/projects

在 Spring 这个庞大的生态系统中,我们还会接触到 Spring Data 和 Spring Boot。这两个项目在应用开发中经常用到,因此从一开始就了解它们非常重要。

通过Spring Data扩展持久化能力#

Spring Data 项目是 Spring 生态系统的一部分,它让你能够轻松连接数据库,并以极少的代码实现持久化层的功能。该项目支持 SQLNoSQL 技术,构建了一个高层次的抽象层,大大简化了数据持久化的开发方式。

Note

我们有 Spring Data Access,它是 Spring Core 的一个模块,同时在 Spring 生态系统中还有一个独立的项目叫做 Spring DataSpring Data Access 提供了基础的数据访问实现,比如事务机制JDBC 工具。而 Spring Data 则进一步增强了对数据库的访问能力,提供了更丰富的工具集,让开发变得更加便捷,也让你的应用能够连接到不同类型的数据源。关于这个话题,我们会在第14章详细讨论。

Spring Boot#

Spring Boot 是 Spring 生态系统中的一个项目,引入了约定优于配置的理念。这个理念的核心在于, 你无需手动配置框架的所有细节,Spring Boot 会为你提供一套默认配置,你只需根据实际需求进行个性化调整 。这样一来,你可以大大减少代码量,因为你遵循了通用的约定,应用之间的差异也会变得很小。因此,与其为每个应用都从零开始配置,不如直接采用默认配置,只针对与约定不同的部分进行修改,这样效率更高。关于 Spring Boot 的更多内容,我们将在第七章详细介绍。

Spring 生态系统庞大,包含了众多项目。有些项目你会经常接触,而有些如果你的应用没有特定需求,可能根本用不到。在本书中,我们只涉及那些帮助你入门所必需的项目: Spring CoreSpring DataSpring Boot。你可以在 Spring 官方网站 https://spring.io/projects/ 上找到 Spring 生态系统中所有项目的完整列表。

关于 Spring 的替代方案#

我们其实很难直接讨论 Spring 的替代品,因为这样容易让人误以为是在寻找整个 Spring 生态系统的替代方案。但实际上,对于构成Spring 生态系统的许多单独组件和项目,你都可以找到其他选择,比如其他开源或商业框架和库。

举个例子,比如 Spring 的 IoC 容器。多年前,Java EE 规范曾是开发者们非常青睐的解决方案。虽然理念略有不同,Java EE(在 2017 年开源并重塑为 Jakarta EE,https://jakarta.ee/)提供了诸如上下文与依赖注入(CDI)企业级 Java Bean(EJB)等规范。你可以使用 CDI 或 EJB 来管理对象实例的上下文,并实现一些切面功能(在 EE 术语中称为 拦截器(interceptors))。此外,历史上 Google Guice(https://github.com/google/guice )也是一个备受推崇的对象实例容器管理框架。

对于某些单独的项目,你可以找到一个或多个替代方案。例如,你可以选择使用 Apache Shirohttps://shiro.apache.org/)来替代 Spring Security,或者决定用 Play 框架https://www.playframework.com/)来实现你的 Web 应用,而不是采用 Spring MVC 及相关技术。

最近有一个颇具前景的项目叫做 Red Hat Quarkus。Quarkus 专为云原生应用而设计,并且正在以非常快的速度不断成熟。我完全不意外它未来会成为 Java 生态中开发企业级应用的主流项目之一。(https://quarkus.io/

我的建议是,你要始终考虑各种备选方案。在软件开发中,保持开放的心态非常重要,千万不要把某一个解决方案当作唯一的选择。你总会遇到某些场景,某项技术比其他技术更适用。

现实场景中的Spring#

现在你已经对 Spring 有了一个整体的了解,也知道了在什么情况下以及为什么要使用这样一个框架。在本节中,我会为你介绍一些适合使用 Spring 框架的应用场景。很多时候,开发者们只把 Spring 这样的框架用于后端应用,甚至有越来越多的人把它局限在后端 Web 应用上。虽然在很多实际案例中,Spring 确实常常被这样使用,但我们也要记住,这个框架远不止于此。我见过一些团队在不同类型的应用中成功地应用了 Spring,比如开发自动化测试工具,甚至是独立的桌面应用程序。

我会进一步为你介绍一些在实际项目中常见且被成功应用Spring的场景。当然,这些并不是唯一的应用场景,在这些情况下Spring也不一定总是适用。还记得我们在1.2节讨论过的内容吗:框架并不总是最佳选择。不过,以下这些场景通常都比较适合使用Spring:

  1. 后端应用的开发
  2. 自动化测试框架的开发
  3. 桌面应用的开发
  4. 移动应用的开发

在后端应用开发中使用Spring#

后端应用是系统中在服务器端运行的部分,负责管理数据并响应客户端应用的请求。用户通过直接使用客户端应用来访问各项功能,而客户端应用则会向后端应用发起请求,以处理用户的数据。后端应用通常会使用数据库来存储数据,或者以不同方式与其他后端应用进行通信。

你可以想象,在现实场景中,这个应用其实就是后台系统,用来管理你银行账户里的各类交易。用户可以通过网页应用(网上银行)或移动应用来访问和管理自己的账户。无论是移动端还是网页端,这些应用都相当于是后台系统的客户端。为了管理用户的交易,后台应用需要与其他后端系统进行通信,同时还需要将部分数据持久化存储到数据库中。在图1.5中,你可以直观地看到这样一个系统的架构。


图1.5 后端应用通过多种方式与其他应用进行交互,并利用数据库来管理数据。通常,后端应用结构复杂,可能需要使用多种技术。框架通过提供实用工具,帮助你更高效地实现后端解决方案,从而简化了开发过程。

Note

如果你看不懂图1.5的所有细节,也不用担心。我并不指望你现在就知道什么是消息中间件,甚至也不需要你了解各个组件之间的数据交换是如何建立的。我想让你明白的是,这样的系统在现实世界中可能会变得非常复杂,而Spring生态中的各个项目正是为了帮助你尽可能简化这种复杂性而诞生的。

Spring为实现后端应用程序提供了一套出色的工具。无论是与其他应用集成,还是在各种数据库技术中实现数据持久化,Spring都能让你轻松应对后端开发中常见的功能需求。难怪开发者们经常选择Spring来构建这类应用。这个框架几乎涵盖了你在后端实现中所需的一切,无论采用哪种架构风格,都是理想的选择。图1.6展示了Spring在后端应用中的多种应用可能性。


图1.6 展示了在后端应用中使用Spring的无限可能性——无论是对外暴露功能接口供其他应用调用,还是管理数据库访问,从应用安全防护到通过第三方消息中间件实现集成,Spring都能轻松胜任。

在自动化测试应用中使用Spring#

如今,我们经常使用自动化测试来对我们实现的系统进行端到端测试。自动化测试是指开发团队实现的软件,用于确保应用程序的行为符合预期。开发团队可以安排自动化测试的执行,定期对应用进行测试,并在出现问题时及时通知开发人员。拥有这样的功能能够让开发者更有信心,因为他们知道,在开发新功能的同时,如果破坏了应用现有的功能,会第一时间收到通知。

对于小型系统,你可以手动进行测试,但自动化测试用例始终是个好主意。对于更复杂的系统,手动测试所有流程几乎不可能。因为流程太多,想要全部覆盖会耗费大量时间和精力。

事实证明,最高效的解决方案是由一个独立的团队开发一个专门的应用程序,负责验证被测试系统的所有流程。当开发人员为系统添加新功能时,这个测试应用也会相应地进行扩展,以覆盖新增内容,团队则利用它来验证一切是否依然按预期运行。开发人员最终会使用集成工具,并定期安排该应用运行,以便尽早获得他们更改的反馈(见图1.7)。


图1.7 团队在测试环境中部署测试应用。像 Jenkins 这样的持续集成工具会定期运行该应用,并将反馈发送给团队。通过这种方式,团队始终能够掌握系统的状态,并在开发过程中及时发现和修复问题。

这样的应用程序可能会变得和后端应用一样复杂。为了验证各类流程,应用需要与系统的各个组件通信,甚至还要连接数据库。有时,应用还会模拟外部依赖,以便测试不同的执行场景。在编写测试场景时,开发者通常会使用像 SeleniumCucumberGauge 等框架。不过,除了这些框架之外,应用其实还能从 Spring 的各种工具中获得不少好处。比如,可以利用 Spring 的 IoC 容器来管理对象实例,让代码更易维护;也可以用 Spring Data 连接数据库,方便数据校验;还可以向消息中间件的队列或主题发送消息,模拟特定场景,或者直接用 Spring 调用一些 REST 接口(见图 1.8)。(如果你觉得这些内容有些超前也没关系,随着你继续阅读本书,相关概念都会逐步讲清楚。)


图 1.8 测试应用可能需要连接数据库,或与其他系统或被测系统进行通信。开发人员可以利用 Spring 生态系统的组件,简化这些功能的实现。

使用Spring开发桌面应用程序#

如今,桌面应用程序的开发已经不如以往常见,因为 Web 应用和移动应用已经成为与用户交互的主要方式。不过,仍然有一部分桌面应用存在,而 Spring 生态系统中的一些组件在开发这些应用的功能时,可能是不错的选择。桌面应用可以很好地利用 Spring 的 IoC 容器来管理对象实例,这样不仅让应用的实现更加简洁,也提升了可维护性。此外,应用还可以借助 Spring 的各种工具来实现不同的功能,比如与后端或其他组件通信(调用 Web 服务或使用其他远程调用技术),或者实现缓存方案等。

在移动应用中使用 Spring#

Spring 社区通过其 Spring for Android 项目,致力于支持移动应用的开发。虽然你可能很少会遇到这种需求,但值得一提的是,你完全可以利用 Spring 的相关工具来开发 Android 应用。这个 Spring 项目为 Android 提供了 REST 客户端,并支持访问受保护 API 的身份认证功能。

什么时候不该使用框架#

在本节中,我们将讨论为什么有时候你应该避免使用框架。了解什么时候该用框架、什么时候不该用框架,这一点非常重要。有时候,使用一款杀鸡用牛刀的工具不仅会消耗更多精力,反而可能带来更糟糕的结果。想象一下,用电锯来切面包。虽然你也许能勉强完成,但这比用普通的刀要费力得多,而且最后可能只剩下一堆面包屑,而不是整齐的面包片。

接下来,我们会聊聊在什么情况下不适合用框架,然后我会分享一个亲身经历的故事——我曾经参与的一个团队,因为选用了框架,导致一个应用的开发以失败告终。

事实证明,就像软件开发中的其他事情一样,框架并不是在所有情况下都适用。你会遇到一些场景,框架并不合适——或者说,虽然框架本身合适,但 Spring 框架却未必适用。以下哪些情况下,你应该考虑不使用框架?

  1. 如果你需要实现某项功能,并且希望应用程序文件所占用的存储空间尽可能小。这里所说的占用空间,指的是应用文件所占用的存储内存。
  2. 某些特定的安全要求,要求你只能在应用中编写自定义代码,不能使用任何开源框架。
  3. 如果你在框架基础上需要做大量定制,导致你写的代码比完全不用框架还要多。
  4. 你已经有一个可用的应用,切换到框架并不会带来任何实际好处。

下面我们来详细讨论这些情况。

你需要保持应用的体积小巧#

第一点是指在某些情况下,你需要让你的应用程序尽可能精简。在当今的系统中,越来越多的服务都是通过容器来交付的。你可能已经听说过像 Docker、Kubernetes 这样的容器技术,或者其他相关的术语(如果没有听说过也没关系)。

容器的相关内容非常广泛,超出了本书的讨论范围,所以目前你只需要了解一点:当你采用这种部署方式时,应用程序越小越好。容器就像一个盒子,你的应用程序就运行在里面。关于在容器中部署应用,有一个非常重要的原则:容器应该可以随时销毁和快速重建。应用的体积(占用空间)在这里非常关键。应用越小,初始化所需的时间就越短,可以节省不少启动时间。当然,这并不意味着你在容器中部署的所有应用都不能用框架。

但对于某些应用来说,尤其是那些体积本身就很小的应用,与其引入各种框架的依赖,不如优化它们的初始化过程,尽量减小它们的资源占用。这样的应用有一种被称为无服务器函数(server-less function)的类型。这些无服务器函数其实就是部署在容器中的微型应用程序。由于你无法过多干预它们的部署方式,这些应用看起来就像是无服务器运行的(这也是它们名字的由来)。这类应用需要保持体积小巧,因此在这种特定场景下,你应尽量避免引入框架。考虑到它们本身就很小,实际上你很可能根本不需要用到框架。

出于安全需求,必须编写定制代码#

我在第二点中提到,在某些特定情况下,应用程序由于安全要求无法使用框架。这种情况通常出现在国防或政府机构领域的应用中。当然,这并不意味着所有政府机构使用的应用都禁止使用框架,但对于某些应用,确实会有相关限制。你可能会好奇原因。举个例子,比如使用了像 Spring 这样的开源框架。如果有人发现了某个特定的漏洞,这个漏洞就会被公开,黑客就有可能利用这些信息进行攻击。有时候,这类应用的相关方希望确保系统被入侵的可能性降到几乎为零。为此,他们甚至会选择自己重写某些功能,而不是直接采用第三方的解决方案。

Note

等等!我之前提到,使用开源框架更安全,因为如果存在漏洞,总会有人发现。其实,如果你投入足够的时间和金钱,自己也能做到这一点。总体来说,使用框架当然成本更低。如果你不打算特别谨慎,采用框架更为合理。但在某些项目中,相关方确实希望确保任何信息都不会泄露。

现有的自定义内容过多会让框架变得不切实际#

还有一种情况(第三点),你可能需要避免使用框架,那就是你需要对框架的组件进行大量自定义,结果写的代码比不用框架还要多。正如我在1.1节中提到的,框架为你提供了一些组件,你需要将这些组件与自己的业务代码组装起来,最终实现一个应用。但这些由框架提供的组件往往并不完全契合你的需求,你需要以各种方式对它们进行定制。对框架的组件以及它们的组装方式进行一定程度的自定义是很正常的,但如果你发现自己需要做大量的定制,甚至比从零开发还要复杂,那很可能是你选错了框架(可以考虑寻找其他替代方案),或者根本就不应该使用框架。

你不会因为切换到一个新框架而获益#

在第四点中,我提到一个常见的错误,就是试图用某个框架去替换应用中已经存在并且运行良好的东西。有时候,我们会被诱惑,想要用新的架构替换掉现有的架构。一个新框架出现了,它很流行,大家都在用,那我们为什么不也把自己的应用切换到这个框架上呢?你当然可以这么做,但你需要认真分析,改变一个本来就能正常工作的东西,究竟能带来什么好处。

在某些情况下,比如我在1.1节中讲到的故事,确实有可能通过让你的应用依赖某个特定框架而获得帮助。只要这种改变能带来实际好处,那就去做!比如你想让应用更易维护、更高性能或者更安全,这些都是合理的理由。但如果这种改变并不能带来实际好处,有时候甚至还会带来不确定性,那么到头来你可能会发现,自己投入了时间和金钱,结果反而更糟。

让我给你讲一个我自己的经历。

一次本可避免的错误

使用框架并不总是最佳选择,而我也是吃了这个亏才明白的。几年前,我们在开发一个 Web 应用的后端。时代在变,软件架构也随之演进。那个应用直接用 JDBC 连接 Oracle 数据库,代码写得相当难看。每当需要查询数据库时,应用就会打开一个 statement,然后发送一条 SQL,有时候还要分好几行写。也许你还没遇到过在项目里直接用 JDBC,但相信我,那代码又长又乱,看着就头疼。

当时,市面上已经有一些用不同方法操作数据库的框架逐渐流行起来。我记得第一次接触 Hibernate 的时候。Hibernate 是一个 ORM 框架,可以把数据库里的表和它们之间的关系当作对象和对象之间的关系来处理。用得好的话,代码量会大大减少,功能也更直观。但如果用得不好,反而会拖慢应用速度,让代码更难懂,甚至引入 bug。

我们开发的应用正需要做些调整。我们都知道那堆丑陋的 JDBC 代码有改进空间,至少可以把代码行数降下来,这样维护起来也更轻松。我们几个开发者建议用 Spring 提供的 JdbcTemplate(你会在第 12 章学到这个工具)。但也有人强烈主张用 Hibernate。毕竟它很流行,为什么不用呢?(其实现在它依然是最受欢迎的同类框架之一,你会在第 13 章学到如何和 Spring 集成。)

我当时就觉得,完全换一种方法重写代码肯定很有挑战,而且看不到什么实际好处。更别说这样做还更容易引入 bug。幸运的是,最后我们先做了个概念验证。几个月下来,大家投入了不少精力,也挺焦虑,最终团队决定放弃。权衡各种选择后,我们还是用 JdbcTemplate 完成了重构。我们成功把代码写得更简洁,去掉了大量冗余代码,而且也没必要为这次改动引入新的框架。

你将在本书中学到什么#

既然你翻开了这本书,我猜你很可能是一位在 Java 生态圈工作的软件开发者,并且意识到学习 Spring 很有用。本书的目的,就是在假设你对各种框架——当然也包括 Spring——一无所知的前提下,带你打好 Spring 的基础。这里所说的 Spring,不仅仅指框架的核心部分,而是整个 Spring 生态系统。

当你读完本书后,你将学会以下内容:

  • 使用 Spring 上下文,并围绕由框架管理的对象实现切面编程;
  • 实现 Spring 应用程序连接数据库并操作持久化数据的机制;
  • 通过 Spring 实现的 REST API,在应用程序之间建立数据交换;
  • 构建采用约定优于配置原则的基础应用程序;
  • 在 Spring 应用的标准类设计中运用最佳实践;
  • 正确地测试你的 Spring 实现。

总结#

  • 应用框架是一组通用的软件功能,为开发应用程序提供了基础结构。框架就像是搭建应用程序的骨架支撑
  • 框架通过提供可直接组装到你实现中的功能,帮助你更高效地构建应用,而无需从零开发这些功能。使用框架不仅节省时间,还能减少出现漏洞的可能性。
  • 采用像 Spring 这样广为人知的框架,可以接触到庞大的社区,这意味着遇到问题时,很可能有人也遇到过类似情况。你可以借鉴他人的解决方案,避免重复踩坑,从而节省大量自行研究的时间。
  • 在实现应用时,始终要考虑各种可能性,包括不使用框架。如果决定采用一个或多个框架,也要权衡它们的替代方案。你需要思考框架的用途、使用者规模(社区有多大)、以及它在市场上的存在时间(成熟度)。
  • Spring 不仅仅是一个框架。我们常说Spring 框架是指其核心功能,但实际上 Spring 提供了一个由众多项目组成的完整生态系统,广泛应用于应用开发。每个项目都专注于特定领域,在开发应用时,你可能会结合使用多个项目来实现所需功能。本书将会用到的 Spring 生态系统中的项目如下:
    • Spring Core 构建了 Spring 的基础,提供了诸如上下文、切面以及基础数据访问等功能。
    • Spring Data 提供了一套高级且易用的工具,帮助你实现应用的持久化层。你会发现,使用 Spring Data 操作 SQL 和 NoSQL 数据库都非常简单。
    • Spring Boot 是 Spring 生态系统中的一个项目,它帮助你采用“约定优于配置”的开发方式。
  • 很多时候,学习资料(比如书籍、文章或视频教程)只会用 Spring 举例后端应用。虽然 Spring 在后端开发中确实非常常见,但其实你也可以在其他类型的应用中使用 Spring,比如桌面应用或者自动化测试程序

评论