Java EE互联网轻量级框架整合开发:SSM+Redis+Spring微服务(上下册)
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

第2章
认识SSM框架、Redis和微服务

本章目标

1. 了解Spring IoC和Spring AOP的基础概念

2. 了解MyBatis的特点

3. 了解Spring MVC的特点

4. 了解为什么要使用NoSQL(Redis)及Redis的优点

5. 掌握SSM和Redis的基本结构框图和各种技术的作用

2.1 Spring框架

Spring框架是Java中应用最广的框架。它的成功来自理念,而不是技术本身,它的理念包括控制反转(Inversion of Control,IoC)和面向切面编程(Aspect Oriented Programming,AOP)。

2.1.1 Spring IoC简介

IoC是一个容器,在Spring中,它认为一切Java资源都是Java Bean,容器的目标就是管理这些Bean和它们之间的关系。所以在Spring IoC里面装载着各种Bean,其实也可以理解为Java的各种资源。IoC容器管理Java Bean的构建、事件、行为等。除此之外,各个Java Bean之间会存在一定的依赖关系,比如班级是由老师和学生组成的,假设老师、学生都是Java Bean,那么显然二者之间形成了依赖关系,老师和学生有教育和被教育的关系。这些Spring IoC容器都能够对其进行管理,只是Spring IoC管理对象和其依赖关系不是人为构建的,而是由Spring IoC通过描述构建的,也就是说,Spring是依靠描述来完成对象的构建及其依赖关系的。

比如插座,它依赖国家标准(这个标准可以定义为一个接口——Socket)定义,现有两种插座(Socket1和Socket2),如图1-1所示。

图2-1 使用插座图

有两种插座可供选择,具体使用哪种呢?我们可以通过代码来实现使用插座1(Socket1),如代码清单2-1所示。

代码清单2-1:使用插座1(Socket1)

使用Socket socket = new Socket1();后,国家标准插座接口(Socket)就和插座1(Socket1)捆绑在一起了。这样会有一个弊端:如果使用其他插座,就需要修改代码。在这种情况下,Socket接口和其实现类Socket1耦合了,如果有一天不再使用Socket1,而要使用Socket2,那么就要把代码修改为如代码清单2-2所示。

代码清单2-2:使用插座2(Socket2)

如果有其他更好的插座,那么岂不是还要修改代码?一个大型互联网的对象成千上万,如果要不断修改代码,那么对系统的可靠性将是极大的挑战,Spring IoC可以解决这个问题。

首先,我们不用new的方式构建对象,而使用配置的方式构建对象,然后让Spring IoC容器自己通过配置找到插座。用一段XML描述插座和用户的引用插座1,如代码清单2-3所示。

代码清单2-3:使用Spring IoC注入插座1给用户

请注意这些不是Java代码,而是XML配置文件,换句话说只要把配置切换为

就可以向用户信息中注入插座2,显然,切换插座的实现类十分方便。这个时候Socket接口可以不依赖任何插座,只需要通过配置就能切换,如图2-2所示。

图2-2 Spring的控制反转

图2-2的配置信息是“我要插座2”,相当于XML依赖关系配置,这个时候Spring IoC只会拿到插座2,然后通过国家标准插座接口提供给使用者。换句话说,这是一种被动的行为,通过向IoC容器注入描述信息得到资源(Bean),控制权在Spring IoC容器中,它会根据描述找到使用者需要的资源,这就是控制反转的含义。

这样的好处是Socket接口不再依赖某个实现类,需要使用某个实现类时通过配置信息就可以完成了。想修改或者加入其他资源可以通过配置完成,不需要再用new关键字构建对象,依赖关系也可以通过配置完成,即时地管理它们之间的关系。

用户不需要找资源,只要向Spring IoC容器描述所需资源,Spring IoC就会自己寻找,这就是Spring IoC的理念。这样就把Bean之间的依赖关系解耦了,更容易写出结构清晰的程序。除此之外,Spring IoC还提供对Java Bean生命周期的管理,可以延迟加载,可以在其生命周期内定义一些行为等,更加方便有效地管理和使用Java资源,这些在未来都可以学习到,这就是Spring IoC的魅力。

2.1.2 Spring AOP

IoC的目标是管理Bean,而Bean是Java面向对象编程(OOP)的基础,比如声明一个用户类、插座类等都是基于面向对象的概念。

有些情况是面向对象编程没办法处理的。举个例子,生产部门的订单、生产部门、财务部门三者符合OOP的设计理念。订单发出,生产部门审批通过准备付款,但是财务部门发现订单的价格超过预算,需要取消订单。显然超支限定已经不只影响财务部门了,还会影响生产部门之前所做的审批,需要把它们做作废处理。这个超支影响了订单、生产部门和财务部门3个OOP对象。在现实中,这样的限制的影响跨越了3个甚至更多的对象,并且影响了它们之间的协作。所以只用OOP并不完善,还需要协调它们之间的操作,这便是需要面向切面编程的原因,我们可以通过它去协调多个对象之间的操作,如图2-3所示。

图2-3 Spring面向切面编程的理念

在图2-3中,实线是订单提交的流程,虚线是订单驳回的流程,影响它们的条件是预算超额,这是一个对多个对象产生影响的限制条件。

Spring AOP常用于数据库事务的编程,经常发生类似的情况,例如,我们在更新完数据库后,不知道下一步是否会成功,如果下一步失败,则使用数据库事务的回滚功能回滚事务,使第一步的数据库更新作废。在Spring AOP实现的数据库事务管理中,是以异常信息作为消息的。在默认情况下(可以通过Spring的配置修改),只要Spring接收到了异常信息,就会将数据库的事务回滚,而不需要通过代码实现这个过程,从而保证数据的一致性。比如上面的例子,可用一段伪代码进行一些必要的说明,如代码清单2-4所示。

代码清单2-4:Spring AOP处理订单

这里完全看不到数据库连接、事务获取和关闭的代码,只是在方法上标注了注解@Transactional,不需要再编写麻烦的try...catch...finally...语句了。在现实中,Spring AOP的编程也是如此,关于数据库操作的内容都被Spring AOP封装好了,只要遇到注解@Transactional,就不需要自己编写数据库操作的代码。这样使得开发者不再需要关注功能性的代码,专注于业务代码就可以了。开发者所需要知道的是只要方法中发生了异常,Spring就会回滚事务,而数据库的开闭Spring也会自动完成。当然这段话还不算准确,因为事务和业务是十分复杂的,还有许多细节需要注意,只是在入门的章节没有必要谈得如此复杂,后面会详细剖析它们。

2.2 MyBatis简介

MyBatis的前身是Apache的开源项目iBATIS。iBATIS一词来源于internet和abatis的组合,是一个基于Java的持久层框架。2010年,这个项目由Apache software foundation迁移到Google code,并更名为MyBatis。2013年11月,MyBatis迁移到GitHub上,目前由GitHub维护。

MyBatis的优势在于灵活,它几乎可以代替JDBC,同时提供了接口编程。目前MyBatis的数据访问层DAO(Data Access Objects)是不需要实现类的,它只需要一个接口和XML(或者注解)。MyBatis提供自动映射、动态SQL、级联、缓存、注解、代码和SQL分离等特性,具有使用方便的特点,同时可以对SQL进行优化。因为其具有封装少、映射多样化、支持存储过程、可以进行SQL优化等特点,使得它取代Hibernate成为Java互联网中首选的持久框架。

Hibernate作为一种曾经十分流行的框架,有无可替代的优势,这里有必要讨论一下它和MyBatis的区别。由于MyBatis和Hibernate都是持久层框架,都涉及数据库,所以首先定义一个数据库表——角色表(t_role),其结构如图2-4所示。

图2-4 角色表

根据这个角色表,我们可以用一个POJO(Plain Ordinary Java Object)和这张表定义的字段对应起来,如代码清单2-5所示。

代码清单2-5:定义角色POJO

无论是MyBatis还是Hibernate,都是依靠某种方法,将数据库的表和POJO映射起来,这样程序员就可以通过操作POJO来完成相关的逻辑了。

2.2.1 Hibernate简介

要将POJO和数据库映射起来需要给这些框架提供映射规则,所以下一步要提供映射的规则,如图2-5所示。

图2-5 映射规则

在MyBatis或者Hibernate中可以通过XML或者注解的方式提供映射规则,这里讨论XML方式,因为在MyBatis中,注解方式会受到一定的限制,所以MyBatis通常使用XML方式实现映射关系。

我们把POJO对象和数据库表相互映射的框架称为对象关系映射(Object Relational Mapping,ORM,或O/RM,或O/R mapping)框架。无论MyBatis还是Hibernate都可以被称为ORM框架,只是Hibernate的设计理念是完全面向POJO的,而MyBatis不是。Hibernate基本不再需要编写SQL就可以通过映射关系来操作数据库,是一种全表映射的体现;而MyBatis不同,它需要用户提供SQL运行。

Hibernate是将POJO和数据库表对应的映射文件,如代码清单2-6所示。

代码清单2-6:Hibernate映射文件

先对POJO和角色表进行映射,再对POJO进行操作,从而影响角色表的数据,比如对其增、删、查、改,可以按照如代码清单2-7所示的方式操作。

代码清单2-7:Hibernate通过Session操作数据库数据

这里没有看到SQL,那是因为Hibernate会根据映射关系来生成对应的SQL,程序员不用精通SQL,通过POJO就能够操作对应数据库的表了。

这在管理系统时代是十分有利的。因为管理系统优先考虑的是业务逻辑的实现,然后才是性能,使用Hibernate的建模方式是十分有利于分析业务的,所以Hibernate成为那个时代的主流持久框架。

2.2.2 MyBatis

应该说,MyBatis框架成为当前Java互联网持久框架的首选,与Hibernate不同,MyBatis不屏蔽SQL。不屏蔽SQL的优势在于,程序员可以自己制定SQL规则,无须Hibernate自动生成规则,这样能够更加精确地定义SQL,从而优化性能。它更符合移动互联网高并发、大数据、高性能、快响应的要求。

与Hibernate一样,MyBatis需要一个映射文件把POJO和角色表对应起来。MyBatis映射文件如代码清单2-8所示。

代码清单2-8:MyBatis映射文件

这里的resultMap元素用于定义映射规则,而实际上当MyBatis满足一定的规则时,可以自动完成映射,增、删、查、改对应insert、delete、select、update四个元素,这是十分明显的。

注意mapper元素中的namespace属性,它要和一个接口的全限定名保持一致,里面的SQL的id也需要和接口定义的方法完全保持一致,定义MyBatis映射接口,如代码清单2-9所示。

代码清单2-9:定义MyBatis映射接口

这就定义了MyBatis映射接口。到这里或许初学的读者会有一个很大的疑问,就是是否需要定义这个接口的一个实现类呢?答案是不需要,关于这点我们在讨论MyBatis原理时会进一步解释。

下面就可以通过RoleMapper接口完成角色类的增、删、查、改了,如代码清单2-10所示。

代码清单2-10:MyBatis对角色类的增、删、查、改

显然,MyBatis在业务逻辑上和Hibernate是大同小异的。其区别在于,MyBatis需要提供接口和SQL,这意味着它的工作量会比Hibernate大,但是由于自定义SQL、映射关系,所以其灵活性、可优化性超过了Hibernate。因为一条SQL的性能可能相差十几倍到几十倍,所以互联网的可优化性、灵活性是十分重要的。

2.2.3 Hibernate和MyBatis的区别

Hibernate和MyBatis的增、删、查、改,对于业务逻辑层来说大同小异,对于映射层,Hibernate的配置不需要接口和SQL,而MyBatis是需要的。Hibernate不需要编写大量的SQL就可以完全映射,同时提供了日志、缓存、级联(级联比MyBatis强大)等特性,此外,还提供HQL(Hibernate Query Language)对POJO进行操作,使用十分方便,但是它也有致命的缺陷。

由于无须SQL,当关联的表超过3个的时候,通过Hibernate的级联会造成很多性能的丢失。例如,访问一个财务报表,它会关联财产信息表,财产又分为机械、原料等,显然机械和原料的字段是不一样的,这些关联字段只能根据特定的条件变化而变化,而Hibernate无法支持这样的变化。遇到存储过程,Hibernate也只能作罢。更为关键的是性能,在管理系统时代,对于性能的要求不是那么苛刻,但是在互联网时代,性能就是系统的根本,响应过慢就会降低客户的忠诚度。

以上问题通过MyBatis都可以解决,MyBatis可以自由书写SQL、支持动态SQL、处理列表、动态生成表名、支持存储过程。这样可以灵活地定义查询语句,满足各类需求和性能优化的需要,这些在互联网系统中是十分重要的。

但MyBatis也有缺陷。首先,它要编写SQL和映射规则,其工作量稍微大于Hibernate。其次,它支持的工具很有限,不像Hibernate,有许多的插件可以帮助其生成映射代码和关联关系,即使使用生成工具,往往也需要开发者进行进一步简化,所以MyBatis采用手工编码的方式,工作量相对大些。

对于性能要求不太苛刻的系统,比如管理系统、ERP等推荐使用Hibernate;对于性能要求高、响应快、灵活的互联网系统则推荐使用MyBatis。

2.3 Spring MVC简介

长期以来,Struts 2与Spring的结合一直存在很多问题,比如兼容性和类臃肿。加之近年来Struts 2漏洞问题频发,导致使用率大减。与此同时,生于Spring Web项目的MVC(Model View Controller)框架走到了我们面前,Spring MVC结构层次清晰,类比较简单,并且与Spring的核心IoC和AOP无缝对接,成为互联网时代的主流框架。

Spring MVC是一种MVC模式,在MVC模式里,把应用程序(输入逻辑、业务逻辑和UI逻辑)分成不同的元素,同时提供这些元素之间的松耦合,主要涉及三个概念。

• Model(模型),封装了应用程序的数据和由它们组成的POJO。

• View(视图),负责把模型数据渲染到视图上,将数据以一定的形式展现给用户。

• Controller(控制器),负责处理用户请求,并建立适当的模型把它传递给视图渲染。

在Spring MVC中还可以定义逻辑视图,通过其提供的视图解析器能够很方便地找到对应的视图进行渲染,或者使用消息转换的功能,比如在Controller的方法内加入注解@ResponseBody后,Spring MVC就可以通过其消息转换系统,将数据转换为JSON,提供给前端Ajax或者手机应用使用。

Spring MVC的重点是它的流程和一些重要的注解,包括控制器、视图解析器、视图等重要内容,这些都将在后面进行详细讨论。

2.4 最流行的NoSQL——Redis

Redis是当前互联网世界最为流行的NoSQL(Not Only SQL)。NoSQL在互联网系统中的作用很大,它可以在很大程度上提高互联网系统的性能。它具备一些持久层的功能,也可以作为缓存工具。NoSQL数据库作为持久层,它存储的数据是半结构化的,这就意味着计算机在读入内存中只存在少量规则,因此读入速度更快。相对于那些结构化、多范式规则的关系数据库系统,它更具性能优势。作为缓存,它可以支持数据存入内存,只要命中率高,它就能快速响应,这是因为内存的读/写数据速度是磁盘的数倍到上百倍,其作用如图2-6所示。

图2-6 NoSQL的作用

目前对NoSQL有很多争议,有些人认为它可以取代数据库,而笔者不这么认为,因为数据库系统有更好的规范性和数据完整性,功能更强大,作为持久层更为完善,安全性更高。而NoSQL结构松散、不完整,功能有限,目前尚不具备取代数据库的实力,但是它的高性能、快响应的特性,使它成为一个很重要的缓存工具。

当前Redis成为主要的NoSQL工具,其原因如下。

响应快速:Redis响应非常快,每秒可以执行大约110000个写操作,或者81000个读操作,其速度远超数据库。如果存入一些常用的数据,就能有效提高系统的性能。

支持多种数据类型:包括字符串、哈希结构、列表、集合、可排序集合等。比如字符串可以存入一些Java基础数据类型,哈希结构可以存储对象,列表可以存储List对象等。这使得在应用中很容易根据自己的需要选择存储的数据类型,方便开发。Redis虽然只支持几种数据类型,但是它有两大好处:一是可以满足存储各种常用数据结构体的需要;二是数据类型少,规则就少,需要的判断和逻辑就少,这样读/写的速度就更快。

操作都是原子的:所有Redis的操作都是原子的,从而确保当两个客户同时访问Redis服务器时,得到的是更新后的值(最新值)。在需要高并发的场合可以考虑使用Redis的事务处理一些需要锁的业务。

MultiUtility工具:Redis 可以在如缓存、消息传递队列中使用(Redis支持“发布+订阅”的消息模式),在Web应用中存储会话信息(session),或者记录某个时间段页面点击量等需要短暂使用到的数据。

Redis具备的这些优点使它成为目前主流的NoSQL技术,在Java互联网中得到广泛使用。

2.5 SSM+Redis结构框图及概述

在Java互联网中,将Spring+Spring MVC+MyBatis(SSM)作为主流框架,SSM+Redis的结构框图,如图2-7所示。

图2-7 SSM+Redis结构框图

结合图2-7,下面简单介绍图中各个组件的功能。

• Spring IoC具有资源(Java Bean)管理、整合、即插即拔的功能。

• Spring AOP具有切面管理,特别是数据库事务管理的功能。

• Spring MVC用于把Web开发的模型、视图和控制器分层,组合成一个有机灵活的系统。

• MyBatis提供了一个数据库访问的持久层,通过MyBatis-Spring项目,它能和Spring无缝对接。

• Redis作为缓存工具,具有高速度处理数据和缓存数据的功能,系统在大部分时间只需要访问缓存,无须从数据库磁盘中重复读/写;在一些需要高速运算的场景下,也可以先用它来完成运算,再把数据批量存入数据库,这样能极大地提升互联网系统的性能和响应能力。

在未来我们还会更为详细地讨论图2-7中的各种技术,这也是本书的核心内容。

2.6 Spring微服务

在当今的Java开发领域,人们似乎不提微服务就不好意思说自己是Java程序员,随着移动互联网的兴起,微服务成为这几年Java互联网开发的热点。关于微服务的概念,笔者不打算在这里长篇论述,实际上微服务是没有明确的规范的,只需要满足特定风格就可以称为微服务。笔者喜欢将微服务架构称为一种“带有一定风格的分布式架构”,微服务架构有自己的特点和风格,但是本质上它属于分布式系统的一种。

当前最流行的Java微服务是Pivotal团队提供的Spring Cloud,学习Spring Cloud需要先学习Spring Boot。这里让我们来了解一下Spring Boot和Spring Cloud的概念。

Spring Boot的设计目的是简化新Spring应用的初始搭建及开发过程。该框架使用了特定的方式进行配置,使开发人员不再需要定义样板化的配置。通过这种方式,Spring Boot致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者。简单地说,Spring Boot的设计理念是约定优于配置,在Spring Boot中会提供很多默认的配置,用户可以通过Spring Boot给予的配置项修改这些配置。大家知道在普通开发中,用户需要提供大量的配置和代码去连接数据库和其他资源,而在Spring Boot中就不再需要了,它会提供对应的配置项,用户只需要跟着这些配置项配置数据库的连接和属性就可以了,例如,只需要配置数据库连接和连接池的属性就可以使用了,而无须再编写任何代码,比如下面的配置。

通过这样的配置,就完成了构建数据库连接池的任务,这些是Spring Boot根据用户的配置自动完成的,并不需要用户编写代码,它的作用是尽可能减少用户编写代码的工作,使得开发者能够更快速地开发业务。

Spring Cloud是微服务开发的利器。在微服务中,一个庞大的单体系统会按照业务微服务被拆分为多个服务,每一个服务都是一个独立的产品,可以拥有独立的数据库、服务器和其他资源,可以独立运行。比如一个庞大的电商单体系统可以拆分为用户、产品、资金、商户等服务,需要通过各种组件来管理这些独立的服务,并且将它们联系起来。为此,Spring Cloud提供了服务发现、服务注册、配置中心、消息总线、负载均衡、服务调用、断路器、数据监控等组件。但是请注意,Spring Cloud的这些组件并非都是自己开发的,而是会选用当前开源、口碑较好和经得起实践考验的分布式组件,Spring Cloud会采用Spring Boot风格将这些组件进行封装,所以学习Spring Cloud的基础是Spring Boot。