1理解MVCMVC是一种经典的设计模式,全名为Model-View-Controller,即模型-视图-控制器。其中,模型是用于封装数据的载体,例如,在Java中一般通过一个简单的POJO(PlainOrdinaryJavaObject)来表示,其本质是一个普通的JavaBean,包含一系列的成员变量及其getter/setter方法。对于视图而言,它更加偏重于展现,也就是说,视图决定了界面到底长什么样子,在Java中可通过JSP来充当视图,或者通过纯HTML的方式进行展现,而后者才是目前的主流。模型和视图需要通过控制器来进行粘合,例如,用户发送一个HTTP请求,此时该请求首先会进入控制器,然后控制器去获取数据并将其封装为模型,最后将模型传递到视图中进行展现。综上所述,MVC的交互过程如下图所示:2MVC模式的优点与不足MVC模式早在上个世纪70年代就诞生了,直到今天它依然存在,可见生命力相当之强。MVC模式最早用于Smalltalk语言中,最后在其它许多开发语言中都得到了很好的应用,例如,Java中的Struts、SpringMVC等框架。正是因为这些MVC框架的出现,才让MVC模式真正落地,让开发更加高效,让代码耦合度尽量减小,让应用程序各部分的职责更加清晰。既然MVC模式这么好,难道它就没有不足的地方吗?我认为MVC至少有以下三点不足:1.每次请求必须经过“控制器-模型-视图”这个流程,用户才能看到最终的展现的界面,这个过程似乎有些复杂。2.实际上视图是依赖于模型的,换句话说,如果没有模型,视图也无法呈现出最终的效果。3.渲染视图的过程是在服务端来完成的,最终呈现给浏览器的是带有模型的视图页面,性能无法得到很好的优化。为了使数据展现过程更加直接,并且提供更好的用户体验,我们有必要对MVC模式进行改进。不妨这样来尝试,首先从浏览器发送AJAX请求,然后服务端接受该请求并返回JSON数据返回给浏览器,最后在浏览器中进行界面渲染。改进后的MVC模式如下图所示:也就是说,我们输入的是AJAX请求,输出的是JSON数据,市面上有这样的技术来实现这个功能吗?答案是REST。REST全称是RepresentationalStateTransfer(表述性状态转移),它是RoyFielding博士在2000年写的一篇关于软件架构风格的论文,此文一出,威震四方!国内外许多知名互联网公司纷纷开始采用这种轻量级的Web服务,大家习惯将其称为RESTfulWebServices,或简称REST服务。如果将浏览器这一端视为前端,而服务器那一端视为后端的话,可以将以上改进后的MVC模式简化为以下前后端分离模式:可见,有了REST服务,前端关注界面展现,后端关注业务逻辑,分工明确,职责清晰。那么,如何使用REST服务将应用程序进行前后端分离呢?我们接下来继续探讨,首先我们需要认识REST。3认识RESTREST本质上是使用URL来访问资源种方式。众所周知,URL就是我们平常使用的请求地址了,其中包括两部分:请求方式与请求路径,比较常见的请求方式是GET与POST,但在REST中又提出了几种其它类型的请求方式,汇总起来有六种:GET、POST、PUT、DELETE、HEAD、OPTIONS。尤其是前四种,正好与CRUD(Create-Retrieve-Update-Delete,增删改查)四种操作相对应,例如,GET(查)、POST(增)、PUT(改)、DELETE(删),这正是REST与CRUD的异曲同工之妙!需要强调的是,REST是“面向资源”的,这里提到的资源,实际上就是我们常说的领域对象,在系统设计过程中,我们经常通过领域对象来进行数据建模。REST是一个“无状态”的架构模式,因为在任何时候都可以由客户端发出请求到服务端,最终返回自己想要的数据,当前请求不会受到上次请求的影响。也就是说,服务端将内部资源发布REST服务,客户端通过URL来访问这些资源,这不就是SOA所提倡的“面向服务”的思想吗?所以,REST也被人们看做是一种“轻量级”的SOA实现技术,因此在企业级应用与互联网应用中都得到了广泛应用。下面我们举几个例子对REST请求进行简单描述:REST请求描述GET:/advertisers获取所有的广告主GET:/advertiser/1获取ID为1的广告主PUT:/advertiser/1更新ID为1的广告主DELETE:/advertiser/1删除ID为1的广告主POST:/advertiser创建广告主可见,请求路径相同,但请求方式不同,所代表的业务操作也不同,例如,/advertiser/1这个请求,带有GET、PUT、DELETE三种不同的请求方式,对应三种不同的业务操作。虽然REST看起来还是很简单的,实际上我们往往需要提供一个REST框架,让其实现前后端分离架构,让开发人员将精力集中在业务上,而并非那些具体的技术细节。下面我们将使用Java技术来实现这个REST框架,整体框架会基于Spring进行开发。4实现REST框架4.1统一响应结构使用REST框架实现前后端分离架构,我们需要首先确定返回的JSON响应结构是统一的,也就是说,每个REST请求将返回相同结构的JSON响应结构。不妨定义一个相对通用的JSON响应结构,其中包含两部分:元数据与返回值,其中,元数据表示操作是否成功与返回值消息等,返回值对应服务端方法所返回的数据。该JSON响应结构如下:{meta:{success:true,message:ok},data:...}为了在框架中映射以上JSON响应结构,我们需要编写一个Response类与其对应:publicclassResponse{privatestaticfinalStringOK=ok;privatestaticfinalStringERROR=error;privateMetameta;privateObjectdata;publicResponsesuccess(){this.meta=newMeta(true,OK);returnthis;}publicResponsesuccess(Objectdata){this.meta=newMeta(true,OK);this.data=data;returnthis;}publicResponsefailure(){this.meta=newMeta(false,ERROR);returnthis;}publicResponsefailure(Stringmessage){this.meta=newMeta(false,message);returnthis;}publicMetagetMeta(){returnmeta;}publicObjectgetData(){returndata;}publicclassMeta{privatebooleansuccess;privateStringmessage;publicMeta(booleansuccess){this.success=success;}publicMeta(booleansuccess,Stringmessage){this.success=success;this.message=message;}publicbooleanisSuccess(){returnsuccess;}publicStringgetMessage(){returnmessage;}}}以上Response类包括两类通用返回值消息:ok与error,还包括两个常用的操作方法:success()与failure(),通过一个内部类来展现元数据结构,我们在下文中多次会使用该Response类。实现该REST框架需要考虑许多问题,首当其冲的就是对象序列化问题。4.2实现对象序列化想要解释什么是对象序列化?不妨通过一些例子进行说明。比如,在服务端从数据库中获取了数据,此时该数据是一个普通的Java对象,然后需要将这个Java对象转换为JSON字符串,并将其返回到浏览器中进行渲染,这个转换过程称为序列化;再比如,通过浏览器发送了一个普通的HTTP请求,该请求携带了一个JSON格式的参数,在服务端需要将该JSON参数转换为普通的Java对象,这个转换过程称为反序列化。不管是序列化还是反序列化,我们一般都称为序列化。实际上,SpringMVC已经为我们提供了这类序列化特性,只需在Controller的方法参数中使用@RequestBody注解定义需要反序列化的参数即可,如以下代码片段:@ControllerpublicclassAdvertiserController{@RequestMapping(value=/advertiser,method=RequestMethod.POST)publicResponsecreateAdvertiser(@RequestBodyAdvertiserParamadvertiserParam){...}}若需要对Controller的方法返回值进行序列化,则需要在该返回值上使用@ResponseBody注解来定义,如以下代码片段:@ControllerpublicclassAdvertiserController{@RequestMapping(value=/advertiser/{id},method=RequestMethod.GET)public@ResponseBodyResponsegetAdvertiser(@PathVariable(id)StringadvertiserId){...}}当然,@ResponseBody注解也可以定义在类上,这样所有的方法都继承了该特性。由于经常会使用到@ResponseBody注解,所以Spring提供了一个名为@RestController的注解来取代以上的@Controller注解,这样我们就可以省略返回值前面的@ResponseBody注解了,但参数前面的@RequestBody注解是无法省略的。实际上,看看Spring中对应@RestController注解的源码便可知晓:@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Controller@ResponseBodypublic@interfaceRestController{Stringvalue()default;}可见,@RestController注解已经被@Controller与@ResponseBody注解定义过了,Spring框架会识别这类注解。需要注意的是,该特性在Spring4.0中才引入。因此,我们可将以上代码进行如下改写:@RestControllerpublicclassAdvertiserController{@RequestMapping(value=/advertiser,method=RequestMethod.POST)publicResponsecreateAdvertiser(@RequestBodyAdvertiserParamadvertiserParam){...}@RequestMapping(value=/advertiser/{id},method=RequestMethod.GET)publicResponsegetAdvertiser(@PathVariable(id)StringadvertiserId){...}}除了使用注解来定义序列化行为以外,我们还需要使用Jackson来提供JSON的序列化操作,在Spring配置文件中只需添加以下配置即可:mvc:annotation-drivenmvc:message-convertersbeanclass=org.springframework.http.converter.json.MappingJackson2HttpMessageConver