固定链接 tomcat + spring mvc原理外传:spring mvc与前端的纠葛

tomcat + spring mvc原理外传:spring mvc与前端的纠葛

tomcat + spring mvc原理外传:spring mvc与前端的纠葛

前言

本来准备继续分析spring mvc的核心组件HandlerAdapter的原理,这个组件负责将请求体request交由最终的Handler处理,也就是由业务层编写的接口(Controller中的方法)处理,然后将返回的结果传给展示层解析渲染。HandlerAdapter可以说是和业务代码的直接交互层。但是写到一半无法继续了,因为目前spring mvc存在两种模式:一种是系统内统合后端和前端的功能,spring mvc不仅包括后端的代码逻辑,同时实现最后前端页面的渲染和返回;另一种是spring mvc项目只作为后端,返回json或者xml数据,由浏览器请求数据,展示页面。对于这两种方式在生产环境中应用的比例,目前看来似乎是后者更加广泛。spring mvc之所以称为mvc(model, view 和controller),是因为最初的设计思想是秉承综合处理后端(model、controller)和前端(view),这意味着目前的代码中会存在很多第一种情况的逻辑。如果想要对代码有清晰的理解,还是需要对这两种模式有基础的认识。为了方便学习,兼顾不太了解第一种模式的读者,故加增此篇作为说明。

spring mvc的设计思想

在经典的gof《设计模式》的引言部分,提出了MVC的设计思想,其中M(model)代表数据模型,V(view)代表展示视图,C(Controller)代表对用户输入的响应方式。书中有一个很典型的例子来理解这一模式,如图所示:
tomcat + spring mvc原理外传:spring mvc与前端的纠葛-QQ20200130-1.png
模型(model)中保存了视图中所需要的数据,而视图(view)可以使用这些数据实现最终展示,模型和视图的分离,可以支持同一种数据的多种展示方式,比如图中的表格、柱状图和饼状图等。而Controller的作用表现在交互的过程中,Controller负责接收传入的操作消息,生成数据model,并将model传递给view做展示。
spring mvc的设计是基于MVC的理论。比较明显的是在编写业务代码时,会用到@Controller或者@RestController注解,由此可见业务代码的各种接口综合起来,实际上实现的就是Controller模块。使用@Controller注解的接口,需要对M(model)和V(view)有感知,传入参数中包括model,返回默认为view名。但是目前多使用@RestController,返回为json数据,隐藏了model和view相关的部分,spirng mvc实际工作模式为spring mc。

spring mvc的前后端一体化模式

spring mvc前后端一体的模式比较流行的模板语言包括JSP、FreeMaker和Thymeleaf等。其中如果看了spring boot自动化配置文件——spring-boot-autoconfigure包下的spring.factories,会发现spring boot引入了如下配置:

这说明spring boot本身默认支持Thymeleaf。

spring boot + Thymeleaf的demo

tomcat + spring mvc原理外传:spring mvc与前端的纠葛-tree.png

可以使用spring intializr生成项目spring boot(spring boot基于spring mvc),当然也可以手动生成。项目的整个目录如上图所示。其中main中存放的是java代码,resource中存放项目配置和静态资源。
首先,项目中需要引入Thymeleaf依赖。因为使用简便的嵌入式h2数据库,也需要引入jdbc和h2数据库的依赖。由于我想偷懒,使用代码builder工具,所以也引入了lombok依赖。整个项目的pom.xml内容如下:

schema.sql存储的是数据库的建表语句,spring boot在加载过程中会自动读取这个文件,并执行建表语句:

这样数据库的friends表就会在内存中建起来。这个表主要是存储姓名、电话、email、qq号、wchat字段。

对应的,代码中用Friend类来存储表中读取的每一条数据。Friend.java文件比较简单,唯一注意点是我使用了@Data、@Builder等注解节省代码,这也是为什么引入lombok依赖的原因。

RecordRepository.java使用jdbc查询和存储数据,findAll读取表中的所有记录,sava方法用来存储一条记录。
style.css设置了简单的样式,会用在home.html中。

最后,重点是RecordController.java文件和home.html文件。RecordController是spring mvc中的C,home.html代表了其中的V,model是被RecordController用来向home.html中传递数据的数据结构。
为了方便理解这两个文件,先展示项目运行后的效果和可以做的操作。
tomcat + spring mvc原理外传:spring mvc与前端的纠葛-home.png
这个是项目运行后,在浏览器输入”http://127.0.0.1:8080/show” ,就可以访问home.html展示页面。在页面中输入如图所示的内容,然后点击提交,会在页面下方展示表中所有录入的信息。这中间需要访问两个后端接口,一个是提交表单的POST接口,另一个是展示信息的GET接口。
先看RecordController.java文件中的接口代码。

RecordController类由@Controller注解,其中注入了RecordRepository的bean,分别用来在home方法和submit方法中查询friends表中所有数据和存入单条记录。
home方法对应处理GET类型“/show”url路径的接口访问,入参是map类型的model。model参数并不是由接口传入的,上文中在浏览器中输入的url“http://127.0.0.1:8080/show”并没有带有如何参数。model是由spring mvc框架传入,在方法返回之后,model会自动传递给view进行视图的渲染,这一步会在home.html中有所体现。home方法在查询表中数据之后,所有”friend”数据会存入到Friend类的List中,接着会把List数据输入model,并将索引命名为“friends”。return返回的String为视图名,即“home.html”的名字(view name)。这个view name和model会在后续框架中的视图解析器中使用。
sumbit方法对应处理表单数据的POST接口访问。这里的入参是由POST传入的,对应的参数名即Friend类中的参数名。在表中存入数据之后,return返回依然应该为视图名,不过这里使用了重定向,使用相对url路径重新访问“/show”接口,调用home方法,展示home.html的视图。
接下来看home.html是如何展示和传递数据的。

在html头中,就声明了会使用thymeleaf模板语言。表单数据使用POST方法发送,input标签中的name对应Friend类中的参数名,这样才能在submit方法中接收输入参数。下面是所有friend数据的展示,数据源是”friends”,对应model中“friends”索引,即存入的List数据结构,这里遍历每一个Friend,将对应的name、phone等参数展示出来。
以上即为spring mvc前后端一体的模式,主要特点为spring mvc既需要负责提供后端数据,也需要进行前端的视图渲染,最终会把整个页面的数据返回给浏览器。这种模式前后端的数据交互比较简单便捷,缺点是过于笨重。

spring mvc专职后端模式

另一种模式是spring mvc只负责后端数据处理,所有的视图相关都交给独立部署的前端完成。这种模式比较典型的是RESTful Web服务,主要为JavaScript类型框架,比如Backbone.js、AngularJS、React或者Vue等,提供数据。

spring mvc的RESTful Web服务

不同于传统spring mvc需要依靠view,RESTful Web服务控制器只返回对象,对象数据作为JSON / XML直接写入HTTP响应。本质实现也是在@Controller的基础上,添加@ResponseBody注解,这也是@RestController注解的浅层实现原理。由于这里只介绍模式,底层原理会在HandlerAdapter篇中详细分析,所以不再赘述。
实现RESTful Web服务就非常简单,在上文的项目中直接修改RecordController的@Controller注解为@RestController注解,然后再次运行,访问”http://127.0.0.1:8080/show”, 网页的返回结果变成了“home”。这时所有的前端文件和配置都已经不再发挥作用,因为return返回结果“home”字符串,不再是视图名,而是直接对应HTTP的响应数据,被写入到了ResponseBody中。
当然也可以返回更复杂的数据结构,比如修改代码如下:

tomcat + spring mvc原理外传:spring mvc与前端的纠葛-rest.png

如果返回Friend类实例,实际会生成一个json字符串。
目前比较常用的JavaScript框架,比如React或者Vue可以独立部署,浏览器获取页面后,可通过url访问spring mvc接口,获取json数据后解析。目前这种模式是在生产中比较常见。
作者:孙新
现在注册滴滴云,得10000元立减红包
8月特惠,1C2G1M云服务器 9.9元/月限时抢
滴滴云使者专属特惠,云服务器低至68元/年
输入大师码【7886】,GPU全线产品9折优惠

您的留言将激励我们越做越好