第 1 章 向Spring Framework 5.0 进化

第 1 章 向Spring Framework 5.0 进化

Spring Framework 1.0于2004年3月发布。16年后,Spring Framework仍然是构建Java应用程序的首选框架。

在Java框架这个相对年轻且不断变化的领域,16年已经算是很长一段时间了。

本章首先介绍Spring Framework的核心功能,然后分析它流行的原因以及它如何保持首选框架的地位,接着介绍Spring Framework中的重要模块,随后学习Spring项目,最后介绍Spring Framework 5.0中的新增功能。

本章将回答以下问题。

  • Spring Framework为什么流行?
  • Spring Framework如何适应应用程序架构的不断进化?
  • Spring Framework中有哪些重要的模块?
  • 在一系列Spring项目中,Spring Framework扮演着什么角色?
  • Spring Framework 5.0中有哪些新增功能?

1.1 Spring Framework

Spring网站对Spring Framework的定义如下:Spring Framework为基于Java的现代企业级应用程序提供了全面的编程和配置模型

Spring Framework用于装配Java企业级应用程序。它的主要作用是为连接应用程序的不同组件提供技术衔接。这样,程序员就可以专注于他们的核心工作——编写业务逻辑。

EJB存在的问题

Spring Framework 1.0于2004年3月发布,当时开发企业级应用程序的主要方法是使用Enterprise Java BeansEJB)2.1。

开发和部署EJB是个烦琐的过程。虽然使用EJB可以更轻松地分发组件,但开发和部署这些组件并对其进行单元测试仍是一项挑战。早期版本的EJB(1.0、2.0、2.1)有个复杂的应用程序接口API),导致用户认为它带来的复杂性远大于好处(在大多数应用程序中也确实如此)。

  • 难以进行单元测试。实际上,很难在EJB Container以外进行测试。
  • 需要通过许多不必要的方法实现多个接口。
  • 异常处理冗长而烦琐。
  • 部署描述符极其不便。

于是,Spring Framework这个旨在简化Java EE应用程序开发过程的轻量级框架应运而生。

1.2 Spring Framework为什么流行

Spring Framework 1.0于2004年3月发布。随后的16年中,Spring Framework的普及率日益提高。

Spring Framework变得如此流行的重要原因如下:

  • 简化了单元测试——因为采用了依赖注入;
  • 减少了衔接代码;
  • 架构灵活性;
  • 与时俱进。

下面详细说明每个原因。

1.2.1 简化了单元测试

早期版本的EJB很难进行单元测试。实际上,在容器以外(版本2.1以前)几乎无法运行EJB。对它们进行测试的唯一方法是将其部署到容器中。

Spring Framework引入了依赖注入DI)的概念。第2章会详细介绍依赖注入。

使用依赖注入可以轻松地用Mock对象替代依赖关系,进而完成单元测试。不需要部署整个应用程序即可对其进行单元测试。

简化单元测试有诸多好处:

  • 提高程序员的工作效率;
  • 能更早发现缺陷,从而降低了修复缺陷的成本;
  • 应用程序将自动进行单元测试(可在持续集成模式下运行),防止将来出现缺陷。

1.2.2 减少了衔接代码

在Spring Framework推出之前,典型的J2EE(现在称为Java EE)应用程序包含大量衔接代码。例如,获取数据库连接的代码、异常处理代码、事务管理代码、日志记录代码,等等。

下面来看一个使用预编译语句(prepared statement)来执行查询的简单示例。

PreparedStatement st = null;
try {
      st = conn.prepareStatement(INSERT_TODO_QUERY);
      st.setString(1, bean.getDescription());
      st.setBoolean(2, bean.isDone());
      st.execute();
    }
catch (SQLException e) {
      logger.error("Failed : " + INSERT_TODO_QUERY, e);
 } finally {
            if (st != null) {
       try {
       st.close();
      } catch (SQLException e) {
       // 忽略——无任何操作
      }
   }
 }

在上例中,有4行业务逻辑和超过10行的衔接代码。

使用Spring Framework,可以用下面两行代码实现相同的逻辑。

jdbcTemplate.update(INSERT_TODO_QUERY,
bean.getDescription(), bean.isDone());

Spring Framework如何实现这种简化

在上例中,Spring JDBC(一般还包括Spring)将大多数受检异常转化成了未受检异常。通常,查询失败时,除了结束语句和承认事务失败以外,我们能做的事情不多。但是,我们不必在每个方法中都执行异常处理,而可以集中进行异常处理,然后使用Spring面向切面编程AOP)完成注入。

使用Spring JDBC不需要创建用于获取连接、创建预编译语句等的衔接代码,而可以在Spring上下文中创建jdbcTemplate类,然后在需要时将其注入数据访问对象DAO)类中。

与上例中类似,Spring JMS、Spring AOP和其他Spring模块也有助于减少衔接代码。

Spring Framework让程序员专注于他们自己的主要工作——编写业务逻辑。

避免所有衔接代码还有另一个巨大的好处——减少重复代码。由于可以在一个地方实现所有用于管理事务、处理异常等(通常为所有横切关注点)的代码,因此,代码维护起来也更加轻松。

1.2.3 架构灵活性

Spring Framework采用模块化设计。它包含一组建立在核心Spring模块之上的独立模块。大多数Spring模块是独立的——可以使用其中一个模块,而不必用到其他模块。

下面来看一些示例。

  • 在Web层,Spring提供了它自己的框架——Spring MVC,而它也全面支持Struts、Vaadin、JSF或你选择的任何Web框架。
  • Spring Beans可以用较少代码实现业务逻辑,而Spring还可以集成EJB。
  • 在数据层,Spring用它的Spring JDBC模块简化了JDBC。但是,Spring也全面支持你首选的任何数据层框架——JPA、Hibernate(无论是否采用JPA)或iBatis。
  • 可以选择使用Spring AOP来实现横切关注点(日志记录、事务管理、安全性等),也可以集成完全成熟的AOP实现,如AspectJ。

Spring Framework并不打算满足一切需求。Spring的核心工作是降低应用程序不同组件之间的耦合度并使它们可以进行测试,同时,它还可以全面集成你选择的框架。这意味着你将获得架构灵活性——如果不想使用特定框架,可以用其他框架轻松替代该框架。

1.2.4 与时俱进

Spring Framework 1.0旨在实现应用程序的可测试性,但是,随着时间的推移,它面临着一些新的挑战。为此,Spring Framework不断进化,通过提供灵活性和模块来保持领先优势。以下是一些示例。

  • Java 5引入了注解。Spring Framework(版本2.5,2007年11月发布)先于Java EE为Spring MVC引入了基于注解的控制器模型。使用Java EE的开发人员直到Java EE 6发布(2009年12月,即两年后)才获得类似的功能。
  • Spring Framework先于Java EE引入了许多抽象概念,以将应用程序与具体的实现分离开来。缓存API就是个典型的例子。Spring在3.1版本中提供了透明缓存支持。Java EE为JCache提出了JSR-107(2014年),Spring 4.1提供了类似支持。

Spring的另一个重要特性是它提供了一系列Spring项目,Spring Framework只是其中之一。1.4节会单独介绍不同的Spring项目。下面的示例说明了Spring是如何通过新的Spring项目努力保持领先地位的。

  • Spring Batch定义了一种新方法来构建Java Batch应用程序。直到Java EE 7发布(2013年6月),Java EE才制定了类似的批处理应用程序规范。
  • 随着架构逐渐向云和微服务进化,Spring推出了面向云的新Spring项目。Spring Cloud可以帮助简化微服务开发和部署。Spring Cloud Data Flow为微服务应用程序提供了业务流程。

1.3 Spring模块

Spring Framework的模块化特点是它得到广泛应用的一个重要原因。它高度模块化,提供了20多个模块,并且这些模块都有明确定义的边界。

下图展示了不同的Spring模块,按通常使用它们的应用程序层进行划分。

我们首先介绍Spring核心容器,然后介绍各个应用程序层常用的其他模块。

1.3.1 Spring核心容器

Spring核心容器提供了Spring Framework的核心功能——依赖注入(DI)、IoC控制反转)容器和应用程序上下文。第2章会详细介绍DI和IoC容器。

下表列出了重要的核心Spring模块。

模块/artifact

用途

spring-core

其他Spring模块使用的实用程序

spring-beans

支持Spring bean;与spring-core一起提供Spring Framework的核心功能——依赖注入;帮助实现BeanFactory

spring-context

实现ApplicationContext,它将扩展BeanFactory并支持资源加载、国际化等

spring-expression

扩展EL(JSP中的表达式语言)并为访问和操纵bean属性(包括数组和集合)提供了语言

1.3.2 横切关注点

横切关注点适用于所有应用程序层——日志记录和安全性等。AOP通常用于实现横切关注点。

单元测试和集成测试也属于这一类别,因为它们同样适用于所有层。

下表列出了与横切关注点相关的重要Spring模块。

模块/artifact

用途

spring-aop

利用方法拦截器和切入点,为面向切面编程提供基本支持

spring-aspects

支持与最流行的、功能全面的AOP框架AspectJ进行集成

spring-instrument

提供基本的监测支持

spring-test

为单元测试和集成测试提供基本支持

1.3.3 Web层

除了可以全面集成Struts等常用Web框架外,Spring还拥有自己的MVC框架——Spring MVC。

重要的artifact/模块如下所示。

  • spring-web:提供基本的Web功能,如多文件上传。支持集成Struts等其他Web框架。
  • spring-webmvc:提供功能全面的Web MVC框架——Spring MVC,该框架还提供了实现REST服务的功能。

第3章和第5章会介绍Spring MVC以及如何使用它来开发Web应用程序和REST服务。

1.3.4 业务层

业务层主要负责执行应用程序的业务逻辑。使用Spring时,通常在简单Java对象(POJO)中实现业务逻辑。

Spring Transactionsspring-tx)为POJO和其他类提供声明式事务管理。

1.3.5 数据层

应用程序中的数据层通常负责与数据库或外部接口进行交互。与数据层相关的一些重要Spring模块如下表所示。

模块/artifact

用途

spring-jdbc

围绕JDBC提供抽象层,以避免出现样板代码

spring-orm

用于集成ORM框架和规范——JPA和Hibernate等

spring-oxm

提供一个对象来支持XML映射集成。支持JAXB、Castor等框架

spring-jms

围绕JMS提供抽象层,以避免出现样板代码

1.4 Spring项目

虽然Spring Framework为企业级应用程序的核心功能(DI、Web、数据)奠定了基础,但其他Spring项目可以支持集成并帮助解决企业环境中出现的其他问题——部署、云、大数据、批处理和安全性,等等。

下面列出了一些重要的Spring项目:

  • Spring Boot
  • Spring Cloud
  • Spring Data
  • Spring Batch
  • Spring Security
  • Spring HATEOAS

1.4.1 Spring Boot

在开发微服务和Web应用程序时面临的一些挑战包括:

  • 选择框架并确定兼容的框架版本;
  • 为配置外部化提供机制——可从一个环境转向另一环境的属性;
  • 运行状况检查和监视——在应用程序的特定部件关闭时发出警报;
  • 确定部署环境并为其配置应用程序。

通过事先提供一组固有设置来确定应如何开发应用程序,Spring Boot开箱即可解决上述所有问题。

第5章和第7章会详细介绍Spring Boot。

1.4.2 Spring Cloud

整个世界都在向云端迁移,这样讲并不夸张。

云原生微服务和应用程序如今盛行。第4章会详细讨论这一主题。

利用Spring Cloud,Spring正帮助快速简化云应用程序的开发。

Spring Cloud为分布式系统中的通用模式提供了解决方案,它可以帮助开发人员快速创建应用程序来实现通用模式。Spring Cloud中实现的一些通用模式如下所示:

  • 配置管理
  • 服务发现
  • 熔断机制
  • 智能化路由

第9章会详细介绍Spring Cloud及其提供的各种功能。

1.4.3 Spring Data

当前世界有多种数据源——SQL(关系型)数据库和一系列NoSQL(非关系型)数据库。Spring Data试图为不同的数据库提供一致的数据访问方法。

Spring Data可以集成各种规范和数据存储:

  • JPA
  • MongoDB
  • Redis
  • Solr
  • Gemfire
  • Apache Cassandra

它的一些重要功能如下:

  • 通过从方法名称中识别查询,为存储库和对象映射提供抽象层;
  • 简单的Spring集成;
  • 集成Spring MVC控制器;
  • 高级自动审计功能——创建者、创建日期、上次更改者和上次更改日期。

第8章会详细介绍Spring Data。

1.4.4 Spring Batch

当前,企业级应用程序使用批处理程序处理大量数据。这些应用程序的需求极为类似。Spring Batch为具有较高性能要求的大型批处理程序提供了解决方案。

Spring Batch的重要功能如下:

  • 能够启动、停止和重启作业,包括在失败处重启失败的作业;
  • 能够大批量地处理数据;
  • 能够在失败时重试或跳过相关步骤;
  • 基于Web的管理界面。

1.4.5 Spring Security

身份验证(authentication)是指确认用户身份的过程。授权(authorization)是指确保用户能够对资源执行所确定操作的过程。

身份验证和授权是企业级应用程序(包括Web应用程序和Web服务)的重要组件。Spring Security为基于Java的应用程序提供了声明式身份验证和授权。

Spring Security的重要功能如下:

  • 简化身份验证和授权;
  • 全面集成Spring MVC和Servlet API;
  • 支持防范常见的安全攻击——跨站点请求伪造(CSRF)和会话固定攻击;
  • 拥有用于集成SAML和LDAP的模块。

第3章会介绍如何利用Spring Security确保Web应用程序的安全。

第6章会讨论如何使用Spring Security,通过基本身份验证和OAuth身份验证机制来确保REST服务的安全。

1.4.6 Spring HATEOAS

HATEOAS表示超媒体即应用状态引擎(Hypermedia as the Engine of Application State)。虽然听起来很复杂,但它实际上是个非常简单的概念,其主要目的是将服务器(服务的提供方)与客户端(服务的消费方)分离开来。

服务提供方为服务消费方提供关于可以对资源执行哪些操作的信息。

Spring HATEOAS提供了一种实现HATEOAS的途径,特别针对通过Spring MVC实现的REST服务。

Spring HATEOAS的重要功能如下:

  • 简化对指向服务方法的链接的定义,使链接更加可靠;
  • 支持JAXB(基于XML)和JSON集成;
  • 支持服务消费方(客户端)。

第6章会介绍HATEOAS的用法。

1.5 Spring Framework 5.0中的新增功能

Spring Framework 5.0是推出Spring Framework 4.0约4年后的第一次重大Spring Framework升级。在此期间,Spring取得的重大进展之一是推进了Spring Boot项目。下一节会介绍Spring Boot 2.0中的新增功能。

Spring Framework 5.0推出的最强大功能之一是反应式编程(Reactive Programming),直接支持核心反应式编程功能和反应式端点。一些重要的变更如下:

  • 基准升级;
  • JDK 9运行时兼容性;
  • 在Spring Framework代码中使用JDK 8功能;
  • 反应式编程支持;
  • 函数式Web框架;
  • 用Jigsaw实现Java模块化;
  • Kotlin支持;
  • 已停用的功能。

1.5.1 基准升级

Spring Framework 5.0以JDK 8和Java EE 7为基准。基本上,这意味着它不再支持以前版本的JDK和Java EE。

下面列出了Spring Framework 5.0的一些重要基准Java EE 7规范:

  • Servlet 3.1
  • JMS 2.0
  • JPA 2.1
  • JAX-RS 2.0
  • Bean Validation 1.1

受支持的一些Java框架的最低版本出现了许多变化。下面列出了一些受支持的主要框架的最低版本:

  • Hibernate 5
  • Jackson 2.6
  • EhCache 2.10
  • JUnit 5
  • Tiles 3

下面列出了受支持的服务器版本:

  • Tomcat 8.5+
  • Jetty 9.4+
  • WildFly 10+
  • Netty 4.1+(用于使用Spring WebFlux进行Web反应式编程)
  • Undertow 1.4+(用于使用Spring WebFlux进行Web反应式编程)

使用任何上述早期版本的规范/框架的应用程序需要至少升级到上面列出的版本,才能使用Spring Framework 5.0。

1.5.2 JDK 9运行时兼容性

JDK 9已于2017年年中发布。Spring Framework 5.0可在运行时兼容JDK 9。

1.5.3 在Spring Framework代码中使用JDK 8功能

Spring Framework 4.x的基准版本为Java SE 6。这意味着它支持Java 6、Java 7和Java 8。必须支持Java SE 6和Java SE 7,这给Spring Framework代码施加了一定限制,导致它无法使用Java 8中的任何新功能。因此,虽然其他环境已升级到了Java 8,但Spring Framework(至少是主要组件)中的代码仍仅限于使用早期版本的Java。

Spring Framework 5.0的基准版本为Java 8,Spring Framework代码现在已升级为使用Java 8中的新增功能。这会提高框架代码的可读性和性能。所使用的一些Java 8功能如下:

  • 核心Spring接口中的Java 8默认方法;
  • 基于Java 8反射增强功能改进的内部代码;
  • 在框架代码中使用函数式编程——lambda和流。

1.5.4 反应式编程支持

反应式编程是Spring Framework 5.0最重要的功能之一。

微服务架构通常建立在基于事件的通信的基础之上。构建应用程序的目的是对事件(或消息)做出响应。

反应式编程提供了一种替代性编程方式,这种方式专注于构建用于响应事件的应用程序。

虽然Java 8本身并不支持反应式编程,但有许多框架支持反应式编程,具体如下。

  • 反应式流:尝试通过语言中立的方法来定义反应式API。
  • Reactor:Spring Pivotal团队通过Java实现反应式流。
  • Spring WebFlux:可以基于反应式编程开发Web应用程序,并提供一种类似于Spring MVC的编程模型。

第11章会介绍反应式编程以及如何通过Spring WebFlux实现反应式编程。

1.5.5 函数式Web框架

Spring 5也提供了一个基于反应式功能的函数式Web框架。

函数式Web框架可以通过函数式编程来定义端点。下面是一个简单的hello world示例。

RouterFunction<String> route =
route(GET("/hello-world"),
request -> Response.ok().body(fromObject("Hello World")));

函数式Web框架还可用于定义更加复杂的路由:

RouterFunction<?> route = route(GET("/todos/{id}"),
request -> {
   Mono<Todo> todo = Mono.justOrEmpty(request.pathVariable("id"))
   .map(Integer::valueOf)
   .then(repository::getTodo);
   return Response.ok().body(fromPublisher(todo, Todo.class));
  })
 .and(route(GET("/todos"),
 request -> {
   Flux<Todo> people = repository.allTodos();
   return Response.ok().body(fromPublisher(people, Todo.class));
 }))
.and(route(POST("/todos"),
request -> {
  Mono<Todo> todo = request.body(toMono(Todo.class));
  return Response.ok().build(repository.saveTodo(todo));
}));

需要注意的两个重要事项如下:

  • RouterFunction评估匹配条件,以将请求路由到相应的处理程序函数;
  • 我们定义3个端点、2个GET和1个POST,并将它们映射到不同的处理程序函数。

第11章会详细介绍Mono和Flux。

1.5.6 Java通过Jigsaw实现模块化

直到Java 8,Java平台仍未实现模块化。这导致了如下两个重要问题。

  • 平台臃肿:过去几十年中,Java模块化一直未受到关注。但是,随着物联网IOT)和新型轻量级平台(如Node.js)的出现,用户迫切需要解决Java平台的臃肿问题。(初始版本的JDK大小不到10 MB,最新版本的JDK却超过200 MB。)
  • JAR Hell:另一个重要的问题与JAR Hell有关。Java ClassLoader查找某个类时,不会查看该类是否有其他定义可用,而会立即加载找到的第一个类。如果应用程序的两个不同部分需要不同JAR中的同一个类,它们将无法指定要从哪个JAR中加载该类。

开放服务网关规范(OSGi)是1999年制定的规范之一,它旨在帮助Java应用程序实现模块化。

每个模块(称为捆绑包)将定义以下内容。

  • 导入项:模块使用的其他捆绑包。
  • 导出项:此捆绑包导出的包。

每个模块都可以有自己的生命周期。它可以自行安装、启动和停止。

Jigsaw是Java Community ProcessJCP)下的一个项目,它从Java 7开始在Java中引入模块化。它有两个主要目标:

  • 为JDK定义并实现模块化结构;
  • 为在Java平台上构建的应用程序定义模块系统。

Jigsaw将作为Java 9的一个组件,Spring Framework 5.0将为Jigsaw模块提供基本支持。

1.5.7 Kotlin支持

Kotlin是一种静态类型的JVM语言,可编写出表达力强、简短、可读的代码。Spring Framework 5.0全面支持Kotlin。

下面来看一个演示数据类的简单Kotlin程序,如下所示。

import java.util.*
data class Todo(var description: String, var name: String, var
targetDate : Date)
fun main(args: Array<String>) {
  var todo = Todo("Learn Spring Boot", "Jack", Date())
  println(todo)
    //Todo(description=Learn Spring Boot, name=Jack,
    //targetDate=Mon May 22 04:26:22 UTC 2017)
  var todo2 = todo.copy(name = "Jill")
  println(todo2)
     //Todo(description=Learn Spring Boot, name=Jill,
     //targetDate=Mon May 22 04:26:22 UTC 2017)
  var todo3 = todo.copy()
  println(todo3.equals(todo)) //true
}

使用不到10行代码,便创建并测试了1个包含3个属性和以下函数的数据bean。

  • equals()
  • hashCode()
  • toString()
  • copy()

Kotlin是一种强类型语言,但它不需要明确指定每个变量的类型:

val arrayList = arrayListOf("Item1", "Item2", "Item3")
// 类型为ArrayList

使用命名参数可以在调用方法时指定参数名称,提高代码的可读性:

var todo = Todo(description = "Learn Spring Boot",
name = "Jack", targetDate = Date())

通过提供默认变量(it)和方法(如takedrop等),Kotlin简化了函数式编程:

var first3TodosOfJack = students.filter { it.name == "Jack"
 }.take(3)

此外,还可以为Kotlin中的参数指定默认值:

import java.util.*
data class Todo(var description: String, var name: String, var
targetDate : Date = Date())
fun main(args: Array<String>) {
  var todo = Todo(description = "Learn Spring Boot", name = "Jack")
}

所有这些特性将增强代码的简洁性和表达力,希望Kotlin会成为用户争相学习的语言。

第13章会详细介绍Kotlin。

1.5.8 已停用的功能

随着基准显著增强,Spring Framework 5.0成为了一个重要的Spring版本。除了提高Java、Java EE和其他一些框架的基准版本以外,Spring Framework 5.0还取消了对以下框架的支持:

  • Portlet
  • Velocity
  • JasperReports
  • XMLBeans
  • JDO
  • Guava

如果使用了任何上述框架,建议你制订迁移计划并继续使用Spring Framework 4.3——它在2019年仍被支持。

1.6 Spring Boot 2.0的新增功能

第一版Spring Boot于2014年发布。预计Spring Boot 2.0会进行以下重要更新:

  • 基准JDK版本是Java 8;
  • 基准Spring版本是Spring Framework 5.0;
  • Spring Boot 2.0将通过WebFlux支持反应式Web编程。

下面列出了一些重要框架的最低版本:

  • Jetty 9.4
  • Tomcat 8.5
  • Hibernate 5.2
  • Gradle 3.4

第5章和第7章会详细介绍Spring Boot。

1.7 小结

在过去的16年中,Spring Framework已显著改善了用户开发Java 企业级应用程序的体验。Spring Framework 5.0推出了大量新功能,同时显著提高了基准。

后面几章将介绍依赖注入以及如何使用Spring MVC开发Web应用程序,之后将详细介绍微服务。第5章、第6章和第7章将说明如何通过Spring Boot更轻松地创建微服务。最后几章将介绍如何使用Spring Cloud和Spring Cloud Data Flow在云端构建应用程序。

目录

  • 版权声明
  • 前言
  • 第 1 章 向Spring Framework 5.0 进化
  • 第 2 章 依赖注入
  • 第 3 章 使用Spring MVC构建Web应用程序
  • 第 4 章 向微服务和云原生应用程序进化
  • 第 5 章 使用Spring Boot构建微服务
  • 第 6 章 扩展微服务
  • 第 7 章 Spring Boot的高级功能
  • 第 8 章 Spring Data
  • 第 9 章 Spring Cloud
  • 第 10 章 Spring Cloud Data Flow
  • 第 11 章 反应式编程
  • 第 12 章 Spring最佳实践
  • 第 13 章 在Spring中使用Kotlin
  • 作者简介