Spring MVC体系结构(一)

前言

此文章可以带领初学者们搭建Spring MVC项目,文中对MVC模式、Spring MVC的诞生背景、原理、体系结构等做了讲解,并以一个例子帮助理解。此博文中的所有图片,均是博主用脑图工具手动画的(网上的太不清晰了,不好找),转载请标明出处!

目录

前言

一、MVC设计模式杂谈

第一种:JSP Model1

第二种:JSP Model2

MVC处理流程及优缺点

二、Spring MVC介绍及其环境搭建

一、Spring MVC框架介绍

二、Spring MVC环境搭建

三、Spring MVC框架的请求处理流程及体系结构

一、Spring MVC框架的请求处理流程

二、Spring MVC框架的体系结构

三、Spring MVC框架的特点

总结


一、MVC设计模式杂谈

在传统的Web应用开发中,我们的架构模式基本一致:

  • 数据实体:POJO
  • 数据层:DAO
  • 业务层:Service
  • 控制层:Servlet
  • 表示层(页面层):JSP页面或HTML页面

这种架构模式就是MVC设计模式,它是软件工程中的一种架构模式,强制性地使软件系统的输入、处理和输出分开,把系统分为三个基本部分:模型(Model)、视图(View)、控制器(Controller),如下图所示:

 结合上图,我们分析一下MVC模式中各部分的职责:

Model:模型对象拥有最多的处理任务,是应用程序的主体部分,它负责数据逻辑(业务逻辑)的处理和实现数据库的操作。在项目中除了控制层的控制器,几乎每一个Bean组件都属于模型,比如Service层、DAO层,以及POJO实体类等。

View:负责格式化数据并把它们呈现给用户,包括数据展示、用户交互、数据验证、页面设计等功能。说白了就是离用户最近的、展示给人们看的,比如HTML或者JSP页面。

Controller:负责接收并转发请求,对请求处理之后拿到响应结果,指派要使用的视图(类似于指定Servlet跳转到不同的页面进行展示),将响应结果返回给客户端。对应的组件一般是Servlet,很少用JSP页面直接处理其他页面过来的请求。

其实,JavaBean+Servlet+JSP,就是最经典的MVC。下面看一下MVC的两种模式:

第一种:JSP Model1

在一个项目中,如果业务流程比较简单的时候,可以把控制器的功能交给视图,项目架构中只有视图和模型,没有控制器,即JSP+JavaBean,这种模式称为JSP Model1。

 通过上图我们可以发现,Model1模式的基础是JSP,它由JSP和JavaBean组成,JSP从HTTPRequest中获取所需要的数据,并调用JavaBean进行业务逻辑的处理,然后通过HTTPResponse将结果返回给前端浏览器。可见,Model1一定程度上实现了MVC,只不过将控制层和视图层统一定位到JSP页面,JavaBean依然充当模型组件。这种模式下JSP身兼多职,既要负责视图层的数据展示,又要负责业务流程控制,结构较为混乱,也不是我们所希望的松耦合架构,所以在大型项目中或者当业务流程比较复杂的时候不建议这样做。

第二种:JSP Model2

相比于JSP Model1,当业务流程比较复杂的时候,就需要把业务流程控制交给专门的控制器,JSP只专注于视图的渲染展现即可。这种模式就是JSP Model2,即JSP+Servlet+JavaBean,也就是典型的MVC设计模式。

相比于Model1,Model2是将控制层单独划分出来,以Servlet的形式存在于项目架构中,负责业务流程的控制,接收请求,创建所需的JavaBean组件,并将处理后的数据返回给视图(JSP/HTML)进行结果渲染展示。这样的结构比较清晰,效果明显优化很多,并且结合Spring的IoC和AOP,也是一个松耦合的架构模式。所以,除非项目特别简单,一般情况下推荐使用JSP Model2。

MVC处理流程及优缺点

通过以上对MVC的了解,我们不妨分析一下MVC的处理过程:

  1. 首先视图提供系统与用户交互的界面,并发送用户的输入给控制器;
  2. 控制器接收到用户的请求,根据判断,决定调用哪个模型的哪个方法进行处理;
  3. 模型被控制器调用,根据控制器的指令进行相应的业务逻辑处理,并返回处理结果(数据);
  4. 控制器根据返回的结果,调用相应的视图来渲染、格式化模型返回的数据;
  5. 视图响应给客户端浏览器。

以上即是MVC的处理流程以及各部分之间的关系,那么任何一件事都会有利有弊,我们再分析一下MVC模式的优缺点。

优点:

  1. 可以多视图共享多个模型,大大提高了代码的复用性;
  2. MVC的三个模块相互独立,松耦合架构;
  3. 控制器提高了应用程序的灵活性和可配置性;
  4. 有利于项目的管理和维护。

缺点:

  1. 原理较复杂,实现起来固然没有JSP+JavaBean的方式来得快;
  2. 增加了系统结构和实现的复杂性;
  3. 视图对模型数据的访问效率较低。

总之,我们可以通过MVC的设计模式,最终打造出一个松耦合+高复用+高适用性等的完美架构,这也是架构设计的目标之一。但是,对于MVC来说,它并不适用于小型的项目,花费大量的时间将MVC应用到项目中通常得不偿失,夸张点说,可能搭建三层架构、构建MVC开发环境的时间,都可以实现整个项目功能了。所以,是否使用MVC模式,应该根据实际场景来决定。

二、Spring MVC介绍及其环境搭建

一、Spring MVC框架介绍

有了以上对MVC设计模式的认识,将有助于我们理解Spring MVC的理念与原理。

Spring MVC是Spring家族中应用于Web应用的一个模块,是Spring提供的一个基于MVC设计模式的Web开发框架,可以将它理解为Servlet。在MVC模式中,Spring MVC作为控制器(Controller)来建立模型与视图的数据交互,是结构最清晰的JSP Model2实现,可以说是一个典型的MVC框架。

除此之外,Spring MVC框架采用松耦合、可插拔的组件结构,具有高度可配置性,比起其他的MVC框架更具有扩展性和灵活性。并且它本身就是Spring家族的一部分,与Spring框架整合更是天衣无缝。

在Spring MVC框架中,Controller替换Servlet来担负控制器的职责。Controller接收请求,调用相应的Model进行处理,Model处理完之后将结果返回给Controller,Conreoller再指派相应的View对处理结果进行渲染,最终响应给客户端进行展示。

由于Spring MVC结构较复杂,以上只是简单的介绍,接下来我们以一个例子,来搭建Spring MVC环境,从而更深入的了解它的架构模型及请求处理流程。万变不离其宗,依然以编程界的圣经——“Hello World”为例。

二、Spring MVC环境搭建

首先创建Web项目,在项目根目录创建resource文件夹,待会存放我们的配置文件,并创建以下目录结构:

1、准备需要的jar包

本基础示例所需要的jar包如下:

 没有的请自行下载,分享百度网盘下载地址(5.0.8版本):

https://pan.baidu.com/s/1HYqfU85-jPkHmDJeUFoWHA

2、在web.xml中配置Servlet

Spring MVC是基于Servlet的,DispatcherServlet是整个Spring MVC框架的核心,它负责截获请求并将其分派给相应的处理器。那么搭建Spring MVC环境,首先我们要进行DispatcherServlet的配置。同之前配置Servlet一样,因为通过读取源码我们可以发现,DispatcherServlet这个类继承了FrameworkServlet,FrameworkServlet又继承了HttpServletBean,HttpServletBean最终继承了HttpServlet,所以可以说DispatcherServlet本身就是一个Servlet。

在web.xml配置如下:

<web-app>
    <servlet>
        <servlet-name>mvcDemo</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>  <!--指定SpringMVC配置文件的路径-->
            <param-value>classpath:spring-mvc-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>  <!--告诉容器一开始就加载此Servlet-->
    </servlet>
    <servlet-mapping>
        <servlet-name>mvcDemo</servlet-name>
        <url-pattern>/</url-pattern>  <!--截获所有的HTTP请求-->
    </servlet-mapping>
</web-app>

在上述代码中,配置了一个名为mvcDemo的Servlet。该Servlet是DispatcherServlet类型,前面已经介绍过Sprnig MVC本质就是一个Servlet,它是Spring MVC的入口,并通过<load-on-startup>1</load-on-startup>指定容器在启动的时候就创建此Servlet,然后通过servlet-mapping映射到“/”,即所有的URL请求都会被该Servlet截获。

最重要的一点,我们需要设置一个参数contextConfigLocation参数来指定Spring MVC配置文件的路径(我们马上就创建),此处使用资源路径的方式进行指定(classpath:……)。这个参数也可以声明成上下文参数,保证能读取到就行。

3、创建Spring MVC的配置文件

在resource文件夹下创建spring-mvc-servlet.xml配置文件(为了方便与其他框架的集成的时候,各个配置文件能够更好的区分,建议采用此命名规范,当然这个因人而异),内容如下:

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd
">
    <bean name="/welcome" class="com.smbms.controller.WelcomeController" />
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>
</beans>

在上述配置文件,主要包含两部分内容:

1)配置处理器映射

我们在web.xml中配置DispatcherServlet时,配置了所有请求都由此Servlet来处理,那么DispatcherServlet怎么判断它将哪个请求、分发给哪个Controller(控制器)去处理呢?DispatcherServlet需要咨询一个Bean组件,这个Bean叫做HandlerMapping,它的作用就是将一个URL请求映射到相应的Controller,类似于servlet-mapping中的url-pattern。

Spring MVC提供了多种处理器映射(HandlerMapping)的支持,如:

  • org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping;
  • org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
  • org.springframework.web.servlet.mvc.annotation.DefaultAnnontationHandlerMapping;
  • org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,等

可以根据需求选择处理器映射,此处我们采用BeanNameUrlHandlerMapping(默认的),即在Spring容器中查找与请求URL相同名称的Bean。这个映射器不需要配置,根据请求的URL路径映射到控制器Bean的名称。至于其他的处理器映射,在以后相继介绍。

<bean name="/welcome" class="com.smbms.controller.WelcomeController" />

指定这个Bean的类型是我们目录结构下controller包下的WelcomeController类,让这个类作为一个控制器,URL是/welcome的请求将被这个控制器处理。当然,到这一步我们还没有创建这个类,不要急,下一步我们就创建它(本人喜欢从配置文件入手)。

2)配置视图解析器

处理请求的最后一件必要的事情就是渲染输出,返回给客户端显示,这个任务由视图(View)实现。那么就需要确定指定的请求需要哪个视图进行渲染?DispatcherServlet会查找到一个视图解析器,将控制器返回的逻辑视图名称转换成实际视图。Spring也提供了多种视图解析器,如下:

  • org.springframework.web.servlet.view.InternalResourceViewResolver;
  • org.springframework.web.servlet.view.ContentNegotiatingViewResolve,等等

此处我们使用InternalResourceViewResolver定义该视图解析器,通过配置prefix(前缀)和suffix(后缀),最后将视图逻辑名解析为/jsp/xxx.jsp,即定位到jsp文件夹下的.jsp文件。

4、创建Controller

到这一步,Spring MVC的环境已经搭建好了,我们只需要再填入一点缺失的东西,即Controller和View。我们先创建Controller。

在com.smbms.controller目录下新建WelcomeController.java文件,即我们上面配置的处理器映射。如何将一个JavaBean变成可以处理前端请求的控制器?需要继承org.springframework.web.servlet.mvc.AbstractController,并实现其handleRequestInternal方法,代码如下:

package com.smbms.controller;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class WelcomeController extends AbstractController {
    @Override
    protected ModelAndView handleRequestInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        return new ModelAndView("welcome");
    }
}

上述代码中,控制器处理方法的返回值为ModelAndView对象,该对象既包含视图信息,也包含模型数据信息,这样Spring MVC就可以使用视图对模型数据进行渲染。其中“welcome”就是视图的逻辑名,最后由视图解析器处理就会变成/jsp/welcome.jsp。因为我们这个例子并不返回数据给页面,所以Model为空,不进行设置。

说点题外话,ModelAndView,正如其名所示,它代表Spring MVC中呈现视图界面时所使用的Model(模型数据)和View(逻辑视图名)。由于Java一次只能返回一个对象,所以ModelAndView的作用就是封装这两个对象,以方便一次返回我们所需的Model和View。当然,返回的模型跟视图都是可选的,有时候模型中没有任何数据,那么只返回视图即可;或者只返回模型数据,让Spring MVC根据请求URL来决定视图。在以后会对ModelAndView对象进行更详细的讲解。

5、创建View

上面刚说过,我们的Controller返回的逻辑视图名被解析之后,会转换成/jsp/welcome.jsp,所以我们需要在jsp文件夹下存在welcome.jsp文件。在这之前,我们先创建一个main.jsp,作为请求发起的入口(当然你也可以在启动服务之后直接在地址栏访问)。

main.jsp内容如下:

<%@ page language="java" import="java.util.*" contentType="text/html; charset=UTF-8" %>
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <a href="../welcome">去拜访Spring MVC大爷</a>  <%--因为是在jsp目录下,所以先返回上一级目录,回到根目录--%>
</body>
</html>

然后创建welcome.jsp:

<%@ page language="java" import="java.util.*" contentType="text/html; charset=UTF-8" %>
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
<h1>Hello World</h1>
<p>来自Spring MVC大爷的回复</p>
</body>
</html>

6、部署运行

部署你的项目到Tomcat容器,启动项目,点击按钮,如下

 通过上述示例,我们简单总结一下Spring MVC的处理流程:

当用户发起URL请求http://localhost:8088/welcome时,根据web.xml中对于DispatcherServlet的配置,该请求被DispatcherServlet所截获,并根据HandlerMapping找到处理此次请求的相应的Controller(WelcomeController),Controller处理完成之后返回ModelAndView对象,该对象告诉DispatcherServlet需要通过哪个视图来进行数据模型的渲染展示,然后DispatcherServlet拿着这个返回的视图名称去找视图解析器,转换成真正的View(即/jsp/welcome.jsp),返回给浏览器客户端。

7、优化HandlerMapping(处理器映射)

在以上的示例中,我们通过BeanNameUrlHandlerMapping的方式实现请求与Controller之间的映射,但是如果有多种请求,岂不是要在spring-mvc-servlet中配置多个映射关系?针对这种问题,Spring MVC提供了一键式配置方法:<mvc:annotation-driven />,通过注解的方式定义Controller,下面就对以上示例进行优化,结合实例来理解。

修改spring-mvc-servlet.xml配置文件如下:

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd
">
    <context:component-scan base-package="com.smbms.controller" />
    <mvc:annotation-driven />
    <!--<bean name="/welcome" class="com.smbms.controller.WelcomeController" />-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>
</beans>

我们更改了Spring MVC的处理器映射的配置为支持注解式处理器——配置<mvc:annotation-driven />标签。它是Spring MVC提供的一种配置方法,配置此标签之后Spring MVC会帮我们做一些注册组件之类的事,它会根据注解定义的Controller中的处理方法所定义的URL请求路径,自动为我们做URL和Controller之间的映射。具体的实现是,这个标签会自动注册DefaultAnnotationHandlerMapping(处理器映射)和AnnotationMethodHandlerAdapter(处理器适配器),Spring MVC会通过这两个实例完成对@Controller和@RequestMapping等注解的支持,从而找出URL与handler method的关系并予以关联。不是很明白没关系,以后会一点点的推进讲解,更深入地了解它。

然后我们添加了<context:component-scan>标签来扫描我们的controller包,找出使用注解定义的Bean组件,加载到Spring容器,这一点是之前说过的知识点,不再解释。

修改完了配置文件,再去我们的控制器做修改,将WelcomeController.java修改为以下内容:

package com.smbms.controller;


import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class WelcomeController{
    @RequestMapping("/welcome")  //表示与哪个URL请求相对应
    public String welcome(){
        return "welcome";
    }
}

在之前讲解使用注解定义Bean组件的时候说过,@Controller用于定义控制层的组件。这里我们使用@Controller注解将我们的WelcomeController类定义为一个控制器,使其成为一个可以处理HTTP请求的控制器,再使用@RequestMapping注解对它的方法进行标注,确定welcome()方法对应的请求URL,即说明URL为/welcome的请求都将由这个方法进行处理。

这样一来,如果还有其他的业务需求,若不是很复杂,那么就可以直接在该类下增加相应的方法即可,然后再为方法标注@RequestMapping。这样无须创建很多个JavaBean作为Controller,这也是我们经常用的一种方式,支持注解式的处理器。

重新运行项目,与之前的结果是一样的。

注:<mvc:annotation-driven />的原理实现在以后会详解,这里只需要会用就行。

三、Spring MVC框架的请求处理流程及体系结构

一、Spring MVC框架的请求处理流程

通过上面的一个小示例,我们了解了Spring MVC环境的搭建,接下来再深入了解一下它的处理流程。

 分析这张图,可以发现Spring MVC也是一个基于请求驱动的Web框架,并且也使用了前端控制器模式来进行设计,再根据请求映射规则分发给相应的页面控制器(处理器)来进行处理,下面具体分析一下它的处理步骤:

  1. 首先用户发送请求到前端控制器(DispatcherServlet),前端控制器根据请求信息(比如URL)决定将请求分发给哪个页面控制器(Controller)来处理。对应上图中的步骤1、2。
  2. 页面控制器接收到请求之后,进行业务处理,处理完毕之后返回一个ModelAndView。对应上图中的步骤3、4、5。
  3. 前端控制器将控制权收回,然后根据返回的逻辑视图名,通过视图解析器选择真正的视图,将模型数据传入供其渲染展示。对应上图中的步骤6、7。
  4. 前端控制器再次收回控制权,将响应结果返回给浏览器客户端,至此整个流程结束。对应上图中的步骤8。

二、Spring MVC框架的体系结构

根据上述的请求处理流程,我们继续了解下Spring MVC的体系结构。

 通过上图我们发现,从接收请求到返回响应,Spring MVC框架的众多组件通力配合、各司其职地完成整个流程工作。在整个框架中,Spring MVC通过一个前端控制器接收所有的请求,并将具体工作委托给其他组件进行处理,所以说DispatcherServlet处于核心地位,它负责协调组织不同组件完成请求处理并返回响应结果。

根据Spring MVC的请求处理过程,我们具体分析一下每个组件所负责的工作内容:

  1. 客户端发出HTTP请求,Web应用服务器接收此请求。如匹配DispatcherServlet的请求映射路径,则Web容器将该请求转交给DispatcherServlet处理;
  2. DispatcherServlet拿到请求之后,根据请求的信息(URL、请求参数、HTTP方法等)及HandlerMapping的配置找到处理请求的处理器(Handler);
  3. 当DispatcherServlet找到相应的Handler之后,通过HandlerAdapter对Handler进行封装,再以统一的适配器接口调用Handler。HandlerAdapter可以理解为真正使用Handler来干活的人。
  4. 在请求信息真正到达调用Handler的处理方法之前的这段时间,Spring MVC还完成了很多工作,它会将请求信息以一定的方式转换并绑定到请求方法的入参,对于入参的对象会进行数据转换、数据格式化以及数据校验等。这些都做完以后,最后才真正调用Handler的处理方法进行相应的业务逻辑处理。
  5. 处理器完成业务处理之后,将一个ModelAndView对象返回给DispatcherServlet,其中包含了逻辑视图名和模型数据信息。
  6. DispatcherServlet通过ViewResolver将逻辑视图名解析为真正的视图对象View,可以是JSP、HTML、XML、PDF、JSON等等,Spring MVC均可灵活配置,在以后会介绍。
  7. 得到真正的视图对象之后,DispatcherServlet会根据ModelAndView对象中的模型数据对View进行视图渲染。
  8. 最终客户端获得响应消息。

以上是关于Spring MVC的体系结构的分析,我们可以体会到其设计的精妙之处。在后续的博文中,会陆续围绕着它的整个体系结构进行深入讲解。

三、Spring MVC框架的特点

通过上文的演示示例以及对Spring MVC的处理流程、体系结构的介绍,我们可以总结一下它的特点:

  • 角色划分清晰。Model、View、Controller各司其职,耦合度较低。
  • 灵活的配置功能。Spring的核心是IoC和AOP,统一可以实现在MVC上,把各种类当作Bean组件配置在Spring容器中。
  • 提供了大量的接口和实现类,方便各种场景的开发。
  • 真正做到与View层的实现无关。
  • 结合Spring的依赖注入,更好地应用了面向接口编程的思想。
  • 可以与Spring天衣无缝的整合
  • 等等

总之,关于Spring家族的框架,对开发者来说都是一种不错的选择。

总结

Spring MVC结构比较复杂,学习的时候也要掌握方法。首先要明确Spring MVC是一个工具,既然是工具,那么就先学会它的使用方法,不要深陷其细节中,不建议刚开始学习就研究源码,深入浅出,慢慢地在使用过程中加深理解,最后看透源码自然会水到渠成。

  • 38
    点赞
  • 149
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值