【Spring MVC学习笔记 一】Spring MVC基本概念及理论基础

简介: 【Spring MVC学习笔记 一】Spring MVC基本概念及理论基础

终于学习完了Spring的全部内容,那么接下来的精力就集中在了Spring MVC上边,什么是Spring MVC呢,其实Spring MVC就是通过Java实现MVC的轻量级Web框架,既然是Spring MVC,也就是深度兼容到Spring中的。可以这么理解,其实MVC是一种架构思想,实现方式有很多种,之前我们在Java Web基础系列里【Java Web编程 十四】深入理解MVC架构模式通过JSP+Servlet+JDBC组合实现了MVC架构,抑或是我们古早的Struts框架实现MVC架构,其实就是思想的一种实现方式。那么从今天开始我们就使用Spring MVC来代替JSP+Servlet+JDBC组合实现下MVC架构。

回顾MVC思想

MVC 全名是 Model View Controller,一种软件设计典范,用一种业务数据、逻辑、界面显示分离的方法组织代码,各部分职责如下:

  • 视图
    视图是用户看到并与之交互的界面。对老式的Web应用程序来说,视图就是由HTML元素组成的界面,在新式的Web应用程序中,HTML依旧在视图中扮演着重要的角色,但是已经被一些能显示动态数据的JSP逐步取代了,MVC好处是它能为应用程序处理很多不同的视图。在视图中其实没有真正的处理发生,不管这些数据来源是什么,作为视图来讲,它只是作为一种输出数据并允许用户操纵的方式
  • 模型
    模型表示企业数据和业务规则。在MVC的三个部件中,模型拥有最多的处理任务。被模型返回的数据是中立的,就是说模型与数据格式无关,这样一个模型能为多个视图提供数据,由于应用于模型的代码只需写一次就可以被多个视图重用,所以减少了代码的重复性。不能将模型看出一个只有数据的类,其实模型也包含数据和行为,可以认为是领域模型或JavaBean组件(包含数据和行为),现在一般会更加细分:Value Object(数据Dao) 和 服务层(行为Service)。也就是模型提供了模型数据查询和模型数据的状态更新等功能,包括数据业务两部分
  • 控制器
    控制器接受用户的输入并调用模型和视图去完成用户的需求,所以当单击Web页面中的超链接和发送HTML表单时,控制器本身不输出任何东西和做任何处理。它只是接收请求并决定调用哪个模型构件去处理请求,然后再确定用哪个视图来显示返回的数据。

在前后端不分离的站点中,JSP充当了视图,Servlet充当了控制器,Model充当了模型。抽象意义上的职责如下图所示:

现在的MVC模型已经演化为如下操作过程,下面的整个项目就是依据这样的分层构建的

MVC各部分的简要职责以及各个模块活动的数据Model如下:

  • View:视图:显示页面 (返回:DTO转VO
  • Controller:控制器:取得表单数据;调用业务逻辑;转向指定的页面(入参:DTO转PO,返回:PO转DTO
  • Model:模型:定义业务数据模型;处理业务逻辑;持久化数据状态

另外基于传递的数据Model,我们可以把其中涉及的数据模型进行划分:PO,持久对象,对应数据库表的对象模型;DTO,传输对象,前端发给后端的请求对象;VO,视图对象,后端返回给前端的对象

回顾JSP+Servlet+JDBC组合实现方式

整体的项目结构如下,前端页面展示使用了JSP,数据持久化使用了JDBC,Controller使用了Servlet,当然因为项目整体比较简单所以就使用了一个Model贯穿始终,实际上应该按照使用层的不同分别定义的:PO,DTO,VO。

可以看到一个MVC框架的工作主要是这几步:

  1. 将url映射到Java类或Java类的方法
  2. 封装用户提交的数据 (DTO)
  3. 处理请求–>调用相关的业务处理(如需持久化数据封装PO)–>封装响应数据(VO)
  4. 将响应的数据进行渲染 . jsp / html 等表示层数据

了解了一个MVC框架的具体工作流程,我们再来看看Spring MVC是如何进行工作的。

Spring MVC基本概念

Spring MVC是Spring Framework的一部分,是基于Java实现MVC的轻量级Web框架,目前的版本已经迭代到了官方文档-5.2.0.RELEASE

为什么要使用Spring MVC

我们新来看下为什么使用MVC框架,然后看下历史的框架对比Struts和Spring MVC,最后看下为什么我们更多使用Spring MVC

为什么使用MVC框架

为什么使用MVC框架呢, web容器创造了servlet接口,servlet接口就是开发人员自己实现业务逻辑的地方,servlet接口最大的特点就是根据http协议的特点进行定义:

  • 一方面做servlet开发时候如果使用者对http协议特点不是特别熟悉,都会碰到或多或少令人迷惑的问题,特别是碰到一些复杂特殊的请求时候:例如文件上传,返回特殊的文件格式到浏览器,这时候使用servlet开发就不是很方便了,servlet开发还有个问题可能大家常常被忽视,就是请求的数据的类型转化,http协议传输都是文本形式,到了web容器解析后也是文本类型,如果碰到货币,数字,日期这样的类型需要我们根据实际情况进行转化,如果页面传送的信息非常多,我们就不得不做大量类型转化,这种工作没有什么技术含量,是个体力活而且很容易导致程序错误
  • 另一方面,通用的权限校验、日志记录等通用的内容,每个servlet都需要重复处理一遍,浪费时间

那么为什么MVC框架可以解决这些问题呢?这个就需要聊到一个概念:前端控制器。什么是前端控制器?Front Controller模式 要求在WEB应用系统的前端( Front )设置一个入口控制器( Controller ) ,是用来提供一个集中的请求处理机制,所有的请求都被发往该控制器统统一处理 ,然后把请求分发给各自相应的处理程序。如果没有前端控制器,那么每次请求都只能找到对应的Servlet去处理:

类似代码如下,每个请求都需要精准的找到Servlet的地址进行请求:

package com.example.spring_mvc;
import java.io.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
@WebServlet(name = "helloServlet", value = "/hello-servlet")
public class HelloServlet extends HttpServlet {
    private String message;
    public void init() {
        message = "Hello World!";
    }
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setContentType("text/html");
        // Hello
        PrintWriter out = response.getWriter();
        out.println("<html><body>");
        out.println("<h1>" + message + "</h1>");
        out.println("</body></html>");
    }
    public void destroy() {
    }
}

如果有通用的一些代码:如权限检查,授权,日志记录等,每个Servlet都需要重复处理,当然可以使用过滤器去实现,但是实现复杂度还是比较高,所以我们的框架会给我们封装一个前端控制器来解决这个问题:

我们发现这个前端控制器,很像web里面的Filter过滤器:一般的, 我们把处理请求的对象称之为处理器 (Controller)。

  • Apache习惯称之为Action , 如UserAction.
  • Spring习惯称之为 Controller , 如UserController.

从这里能看出使用MVC框架必须在web.xml中配置前端控制器, 一般的要么是要Filter , 要么是Servlet。Struts2基于Filter,SpringMVC基于Servlet

SpringMVC和Struts2对比

SpringMVC和Struts2对比如下,从集成角度和学习成本角度去看,其实Spring MVC更胜一筹

  • Struts2是类级别的拦截, 一个类对应一个request上下文,SpringMVC是方法级别的拦截,一个方法对应一个request上下文,而方法同时又跟一个url对应,所以说从架构本身上SpringMVC就容易实现restful url,而struts2的架构实现起来要费劲,因为Struts2中Action的一个方法可以对应一个url,而其类属性却被所有方法共享,这也就无法用注解或其他方式标识其所属方法了
  • 数据共享方面: SpringMVC的方法之间基本上独立的,独享request response数据,请求数据通过参数获取,处理结果通过ModelMap交回给框架,方法之间不共享变量,而Struts2搞的就比较乱,虽然方法之间也是独立的,但其所有Action变量是共享的,这不会影响程序运行,却给我们编码 读程序时带来麻烦,每次来了请求就创建一个Action,一个Action对象对应一个request上下文
  • 拦截器实现机制方面,Struts2有以自己的interceptor机制,SpringMVC用AOP方式,这样导致Struts2的配置文件量还是比SpringMVC大
  • Ajax的实现方式方面:SpringMVC集成了Ajax,使用非常方便,只需一个注解@ResponseBody就可以实现,然后直接返回响应文本即可,而Struts2拦截器集成了Ajax,在Action中处理时一般必须安装插件或者自己写代码集成进去,使用起来也相对不方便
  • 控制器实现方面: Spring MVC的前端控制器是Servlet, 而Struts2是Filter
  • 参数验证方面:SpringMVC验证支持JSR303,处理起来相对更加灵活方便,而Struts2验证比较繁琐,感觉太烦乱
  • 请求性能方面Spring MVC会稍微比Struts2 快些,Spring MVC是基于方法设计, 处理器是单例;而Sturts2 是基于类设计,每次发一次请求都会实例一个新的Action对象,Action是多例的。

总之SpringMVC目前的使用率已经远远超过了Struts2, 所以我们现在更多使用Spring MVC做框架的MVC,甚至和Spring结合起来,SpringMVC已经解决零配置了

Spring MVC的优点

Spring的web框架围绕DispatcherServlet [ 调度Servlet ] 设计。DispatcherServlet的作用是将请求分发到不同的处理器,Spring MVC有如下的特点:

  • 轻量级,简单易学
  • 高效 , 基于请求响应的MVC框架
  • 与Spring兼容性好,无缝结合
  • 约定优于配置
  • 功能强大:RESTful、数据验证、格式化、本地化、主题等
  • 简洁灵活

正因为SpringMVC简单 , 便捷 , 易学 , 天生和Spring无缝集成(使用SpringIoC和Aop) , 使用约定优于配置 、能够进行简单的junit测试 、支持Restful风格 、异常处理、本地化、国际化 、 数据验证 、 类型转换等,所以我们更倾向于使用Spring MVC

DispatcherServlet中心控制器

Spring MVC框架像许多其他MVC框架一样, 以请求为驱动 , 围绕一个中心Servlet分派请求及提供其他功能,DispatcherServlet是一个实际的Servlet (它继承自HttpServlet 基类)。

当发起请求时被前置的控制器拦截到请求,根据请求参数生成代理请求,找到请求对应的实际控制器,控制器处理请求,创建数据模型,访问数据库,将模型响应给中心控制器,控制器使用模型与视图渲染视图结果,将结果返回给中心控制器,再将结果返回给请求者。这样我们的原有的MVC执行方式如下图:

SpringMVC执行原理

简要分析执行流程:DispatcherServlet表示前置控制器,是整个SpringMVC的控制中心。用户发出请求,DispatcherServlet接收请求并拦截请求。我们假设请求的url为 : http://localhost:8080/spring-mvc/hello,如上url拆分成三部分:

http://localhost:8080   服务器域名
spring-mvc              部署在服务器上的web站点
hello                   表示具体的后端控制器

如上url表示为:请求位于服务器localhost:8080上的spring-mvc站点的hello控制器

如上图拆解的整体请求流程如下:其中DispatcherServlet表示前置控制器,是整个SpringMVC的控制中心。

  1. 用户发出请求,DispatcherServlet接收请求并拦截请求
  2. HandlerMapping为处理器映射。DispatcherServlet调用HandlerMapping,HandlerMapping根据请求url查找Handler
  3. HandlerExecution表示具体的Handler,其主要作用是根据url查找控制器,如上url被查找控制器为:hello
  4. HandlerExecution将解析后的信息传递给DispatcherServlet,如解析控制器映射等
  5. HandlerAdapter表示处理器适配器,其按照特定的规则去执行Handler
  6. Handler让具体的Controller执行
  7. Controller将具体的执行信息返回给HandlerAdapter,如ModelAndView
  8. HandlerAdapter将视图逻辑名或模型传递给DispatcherServlet
  9. DispatcherServlet调用视图解析器(ViewResolver)来解析HandlerAdapter传递的逻辑视图名
  10. 视图解析器将解析的逻辑视图名传给DispatcherServlet
  11. DispatcherServlet根据视图解析器解析的视图结果,调用具体的视图
  12. 最终视图呈现给用户

所以其实Spring MVC的作用就是一个前置的Servlet的集中管理器

总结一下

最初我们使用简单的JSP和Servlet去处理请求,后来发现业务逻辑越来越复杂,耦合度太高,于是我们发明了MVC思想,并使用JSP+Servlet+JDBC组合实现了最初的MVC架构,随着业务的发展我们觉得MVC架构的每个部分都能被框架化,例如持久层就可以使用MyBatis去优化来取代JDBC,Spring接管和取代所有的业务逻辑Model层,甚至兼容整合其它框架,而前置的简单Servlet请求也可以被框架化为Spring MVC去实现。所以重要的其实是MVC思想,从来不是各种各样的框架,框架的都是为了这种思想落地而产生的实际形式,这是框架存在的意义,搞清楚这点,我觉得才有学习的依据。

相关文章
|
7天前
|
前端开发 Java Spring
Spring MVC 是如何对对象参数进行校验的
【6月更文挑战第4天】对象参数校验是使用 SpringMVC 时常用的功能,这篇文章尝试分析了,Spring 是如何实现这一功能的。
20 5
|
1天前
|
前端开发 Java Spring
Spring MVC 请求处理流程
Spring MVC 请求处理流程
4 0
|
5天前
|
JSON 前端开发 Java
Spring MVC 级联对象参数校验
【6月更文挑战第6天】在 Spring MVC 的使用过程中,我们会发现很多非常符合直觉的功能特性,但往往我们会习惯这种「被照顾得很好」的开发方式,依靠直觉去判断很多功能特性的用法。
9 1
|
7天前
|
XML 前端开发 Java
Spring3 MVC中使用Swagger生成API文档
Spring3 MVC中使用Swagger生成API文档
10 0
|
7天前
|
JSON 前端开发 API
Apache HttpClient调用Spring3 MVC Restful Web API演示
Apache HttpClient调用Spring3 MVC Restful Web API演示
11 1
|
7天前
|
前端开发 Java 关系型数据库
在Spring3 MVC中五步配置集成注解方式Hibernate3
在Spring3 MVC中五步配置集成注解方式Hibernate3
18 3
|
8天前
|
JSON 前端开发 Java
记录一次让我吐血的spring3 MVC HTTP406 Json转换错误
记录一次让我吐血的spring3 MVC HTTP406 Json转换错误
6 0
|
8天前
|
前端开发 Java Spring
自定义 Spring MVC Controller 方法参数处理
【6月更文挑战第3天】在 Spring MVC Controller 的方法参数,Spring 会自动为我们注入一些特殊的参数值,比如 HttpServletRequest、HttpServletResponse 等对象,或者 HTTP 请求参数。
49 0
|
8天前
|
前端开发 IDE Java
Spring3 MVC 集成Velocity中文支持
Spring3 MVC 集成Velocity中文支持
27 7
|
8天前
|
Web App开发 前端开发 Java
基于Spring3 MVC实现基于HTML form表单文件上传
基于Spring3 MVC实现基于HTML form表单文件上传
17 7
基于Spring3 MVC实现基于HTML form表单文件上传