Spring MVC 体系结构和处理请求控制器

前言

此篇博文讲解Spring MVC环境的搭建,并实现视图层(View)与控制器(Controller)之间的参数传递。理解MVC的设计模式,Spring MVC的架构以及请求处理流程。Spring MVC 开发环境的搭建,掌握Controller和View之间的映射,掌握参数传递(View-Controller)掌握模型数据处理。

一. MVC设计模式

传统开发中的WEB开发模式基本一样:

  1. 数据访问接口(DAO层)

  2. 处理业务逻辑(Service层)

  3. 数据实体层(POJO层)

  4. 负责前端请求的处理并接受(Servlet 控制层)

  5. 前端页面的展示(JSP 表示层)

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

MVC模式的对应关系

  1. 视图(View):负责格式化数据并把它们呈现给用户,包括数据展示、用户交互、数据验证、页面设计等功能。说白了用户可以操作可以看懂的页面、展示给人们看的,比如HTML或者JSP等等页面。
  2. 控制器(Controller):负责接受转发请求,对请求进行处理后,指派视图并将响应结果发送客户端。对应组件Servlet
  3. 模型(Model):模型对象拥有最多的处理任务,是应用程序的主体部分,它负责数据逻辑(业务逻辑)的处理和实现数据库的操作。在项目中除了控制层的控制器,几乎每一个Bean组件都属于模型,比如Service层、DAO层,以及POJO实体类等。

MVC的两种模式

  1. Model1(模式一)(了解即可,一般不会用)

    当业务流程较为简单的时候,可以把控制器的功能交给视图来实现,故模式一只有视图层和模型,没有控制器。如图:
    Spring MVC 体系结构和处理请求控制器_第1张图片

  2. Model2(模式二) 相当于(JavaBean+Servlet+Jsp)
    相比于JSP Model1,当业务流程比较复杂的时候,就需要把业务流程控制交给专门的控制器,JSP只专注于视图的渲染展现即可。这种模式就是Model2,即JSP+Servlet+JavaBean,也就是典型的MVC设计模式。如图:
    Spring MVC 体系结构和处理请求控制器_第2张图片(引用别人的图片)

总结

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

2. 优点

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

3. 缺点

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

对于MVC来说,它并不适用于小型的项目,花费大量的时间将MVC应用到项目中通常得不偿失,夸张点说,可能搭建三层架构、构建MVC开发环境的时间,都可以实现整个项目功能了。所以,是否使用MVC模式,应该根据实际场景来决定。


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

上面介绍的是MVC设计模式,是一种理念,下面介绍MVC框架

1. Spring MVC框架介绍

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

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

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

2. Spring MVC环境搭建

1. 下载Spring MVC所需的jar文件并导入工程包
  • 在前面章节中的JAR添加以下两个Jar包

  • spring-web-3.2.13.RELEASE.jar

    在Web应用程序开发时,使用Spring框架所需的核心类

  • spring-webmvc-3.2.13.RELEASE.jar

    Spring MVC框架相关的所有类,包含框架的Servlet,Web MVC框架,以及对控制器和相关视图的支持。

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

      <servlet>
            <servlet-name>IndexControllerservlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
            <init-param>
                <param-name>contextConfigLocationparam-name>
                <param-value>classpath:springmvc-servlet.xmlparam-value>
            init-param>
            <load-on-startup>1load-on-startup>
        servlet>
        <servlet-mapping>
            <servlet-name>IndexControllerservlet-name>
            <url-pattern>/url-pattern>
        servlet-mapping>
    

    (1)配置了一个名为mvcDemo的Servlet。

    (2)该Servlet是DispatcherServlet类型,前面已经介绍过Sprnig MVC本质就是一个Servlet,它是Spring MVC的入口,
    (3)通过1指定容器在启动的时候就创建此Servlet,

    (4)然后通过servlet-mapping映射到 "/",即所有的URL请求都会被该Servlet截获。

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

  2. 创建Spring MVC 的配置文件(springmvc-servlet.xml)

    
    <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: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.bdqn.controller.IndexController"/>
    	
    	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
    		<property name="prefix" value="/WEB-INF/jsp/"/>
    		<property name="suffix" value=".jsp"/>
    	bean>
    
    beans>
    
  • 配置处理映射器

    我们在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默认使用 BeanNameUrlHandlerMapping 即在Spring容器中查找与请求URL相同名称的Bean。这个映射器不需要配置,根据请求的URL路径映射到控制器Bean的名称。至于其他的处理器映射,在以后相继介绍。

    <bean name="/welcome" class="com.bdqn.controller.IndexController"/>
    
  • 配置视图解析器
    处理请求的最后一件必要的事情就是渲染输出,返回给客户端显示,这个任务由视图(View)实现。DispatcherServlet会查找到一个视图解析器,将控制器返回的逻辑视图名称转换成实际视图。Spring也提供了多种视图解析器,如下:

    org.springframework.web.servlet.view.InternalResourceViewResolver;
    org.springframework.web.servlet.view.ContentNegotiatingViewResolve
    

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

3. 创建Controller(处理请求的控制器,定义DispatcherServlet)

将一个JavaBean变成可以处理前端请求的控制器需要继承org.springframework.web.servlet.mvc.AbstractController,并实现其handleRequestInternal方法,代码如下:

package com.bdqn.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;

//控制器:IndexController 继承AbstractController实现handleRequestInternal
public class IndexController extends AbstractController{
	
	@Override
	protected ModelAndView handleRequestInternal(HttpServletRequest arg0,HttpServletResponse arg1) throws Exception {
		System.out.println("学框架就学Spring MVC!");
		return new ModelAndView("welcome");
	}
}

控制器处理方法的返回值为ModelAndView对象,该对象既包含视图信息,也包含模型数据信息,这样Spring MVC就可以使用视图对模型数据进行渲染。其中“welcome”就是视图的逻辑名,最后由视图解析器处理就会变成/WEB-INF/jsp/welcome.jsp

视图逻辑名:就一个单纯的名字,没有后缀,就是逻辑上的一个视图名字

注意:

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

4. 创建View

我们的Controller返回的逻辑视图名被解析之后,会转换成"/WEB-INF/jsp/welcome.jsp",所以我们需要在jsp文件夹下存在index.jsp文件。

5. 部署运行

Spring MVC 的处理流程:

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

更改HandlerMapping(处理器映射)

但是如果有多种请求,岂不是要在 springmvc-servlet 中配置多个映射关系?针对这种问题,Spring MVC提供了一键式配置方法:,通过注解的方式定义Controller,结合实例来理解。


<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: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.bdqn.controller">context:component-scan>
	<mvc:annotation-driven/>

	
	
	
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
		<property name="prefix" value="/WEB-INF/jsp/"/>
		<property name="suffix" value=".jsp"/>
	bean>

beans>
package com.bdqn.controller;

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

@Controller
public class IndexController2 {

    @RequestMapping("/welcome2")
    public String ZhuController(){
        System.out.println("注解进入");
        return "welcome1";
    }
}

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

  1. 分析Spring MVC请求处理的流程步骤
    Spring MVC 体系结构和处理请求控制器_第3张图片

  2. Spring MVC框架的体系结构
    Spring MVC 体系结构和处理请求控制器_第4张图片
    (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)最终客户端获得响应消息。

  3. Spring MVC框架的特点

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


三. 参数传递

  • 接下来我们深入学习参数传递,包括View层如何把参数值传递给Controller,以及Controller又如何把参数值传递给View展现。
  • 使用@RequestMapping来映射请求以及使用@RequestParam来绑定请求参数值,两者结合使用,实现灵活的参数传递。
  • Spring MVC 框架提供了功能强大的注解,大大简化了代码开发的同时也提升程序的可扩展性。

1. 参数传递(View to Controller)

View to Controlle 方式:

  1. 简单粗暴的方式将Controller方法中的参数直接入参。但是如果Controller中有参数,URL地址中必须带参数,不然报错。
    Spring MVC 体系结构和处理请求控制器_第5张图片
@RequestMapping:
作用:负责 将不同请求映射到对应的控制器方法上
参数
  1. value: 指定 URL 请求的实际地址,是 @RequestMapping 的默认值。

    @RequestMapping("hello")
    public String hello(){
        System.out.println("hello world");
        return "index";
    }
    

    等同于:

    @RequestMapping(value="hello")
    public String hello(){
        System.out.println("hello world");
        return "index";
    }
    
  2. method:指定请求的 method 类型,GET、POST、PUT、DELETE 等。

    @RequestMapping(value="/postTest",method=RequestMethod.POST)
    public String postTest(){
        System.out.println("postTest");
        return "index";
    }
    

    表示只有 POST 请求可以访问该方法,若使用 GET 请求访问,直接报错。

    Spring MVC 体系结构和处理请求控制器_第6张图片
    其他几种方式同理,限定了请求的类型,我们常用的是 GET 和 POST、PUT,DELETE 是 rest 风格的表现形式。

  3. params: 指定 request 中必须包含某些参数值,否则无法调用该方法。

    @RequestMapping(value="paramsTest",params={"name","id=10"})
    public String paramsTest(){
        System.out.println("paramsTest");
        return "index";
    }
    

    URL 请求中必须包含 name 和 id 两个参数,并且 id 的值必须为 10,才能调用 paramsTest 方法。
    Spring MVC 体系结构和处理请求控制器_第7张图片
    Spring MVC 体系结构和处理请求控制器_第8张图片
    Spring MVC 体系结构和处理请求控制器_第9张图片

    上述 3 种常见的错误,请求必须同时包含 name 和 id 参数,并且 id=10 才可正常访问业务方法。

    @RequestMapping可以在控制器的类定义处指定URL
    在控制器的类定义处以及方法定义处都可添加 @RequestMapping,在类定义处添加 @RequestMapping 注解,相当于多了一层访问路径

    @Controller
    @RequestMapping("/helloHandler")
    public class HelloHandler {
    
         @RequestMapping(value="hello")
          public String hello(){
              System.out.println("hello world");
              return "index";
          }
    }
    

    Spring MVC 体系结构和处理请求控制器_第10张图片

(2)通过请求参数、请求方法、请求URL进行映射

	@RequestMapping(value = "welcome",method = RequestMethod.GET,params = "username")
    public String welcome(@RequestParam String username){
        System.out.println("welcome--->"+username);
        return "welcome2";
    }

@RequestMapping的value表示请求的URL,method表示请求方法,此次设置了GET,所以POST请求是进不来的,param表示请求参数,此处我们的参数名必须包含username。 下面分析一下:

首先,value(请求路径)匹配,method(超链接是GET请求)匹配,请求参数(username)也匹配,所以进入了处理方法。

@RequestParam

在方法入参处使用@RequestParam注解指定其对应的请求参数。@RequestParam有以下三个参数:

  • value:参数名
  • required:是否是必需的,默认为true,表示请求中必须包含对应的参数名,否则抛出异常
  • defaultValue:默认参数名,不推荐使用,比较混乱
@RequestMapping(value = "/welcome",method = RequestMethod.GET)
public String welcome(@RequestParam(value = "name",required = false) String userName){
    //如果方法的入参名与传来的参数名一致,可省略@RequestParam注解
    System.out.println(userName);
    // model.addAttribute("userName",name);
    return "welcome";
}

这种情况下不要求方法的入参名与请求中的参数名一致,保证@RequestParam注解能够将二者关联上即可。但是为了规范起见,建议一致。

2. 参数传递(Controller to View)

了解完从View到Controller的传参,接下来学习Controller到View的传参。

这就需要模型数据的处理了,对于MVC框架来说,模型数据是最重要的一部分,因为控制层是为了产生模型数据(Model),而视图(View)最终也是为了渲染模型数据并进行输出。那么如何将模型数据传递给视图?Spring MVC提供了多种方式,介绍一下比较常用的:

1. ModelAndView

控制器的处理方法的返回值若是ModelAndView,即既包含Model,也包含View。那么拿到该对象之后,Spring MVC就可以使用视图对模型数据进行渲染

ModelAndView对象的常用方法如下:

添加模型数据

  • addObject(String attributeName,Object attributeValue); 该方法的第一个参数为key值,第二个参数为key对应的value。其中key的值可以随便指定,只要保证在该Model内唯一即可。
  • addAllObjects(Map modelMap); 从此方法可以看出,模型数据也是一个Map对象,我们可以添加Map到Model。

设置视图

  • setView(View view); 指定一个具体的视图对象。
  • setViewName(String viewName); 指定一个逻辑视图名。
2. Model

Spring MVC在调用方法前会创建一个隐含的模型对象,作为模型数据的存储容器,一般称为“隐含模型”。若处理方法的入参为Model类型,Spring MVC会将这个隐含模型的引用传递给这些入参,这样开发者就可以通过一个Model类型的入参,访问到模型中的所有数据,当然也可以做修改、添加等。

不管是ModelAndView还是Model,它们的用法很类似,运用起来也比较灵活。Model对象的addAttribute()方法和ModelView对象的添加模型数据的方法的用法是一样的,即Model对象也是一个Map类型的数据结构。

另外,对于Model对象的addAttribute()方法,key的指定并不是必须的,可以不指定。那么既然它是一个Map结构,如果不指定它会以什么为key呢?这种情况下,会默认使用对象的类型作为key。比如上面的示例中,我们的name是字符串类型的,我们如果不指定key,则“string”就是它的key,访问的时候即是${string}。不过个人不推荐此用法,容易混乱。

除此之外,addAttribute()方法可以添加任何数据类型,JavaBean、List、Map等都可以。

3. Map

不拿发现,Spring MVC的Model其实就是一个Map数据结构,故我们使用Map为处理方法入参也是可行的。

Spring MVC控制器的处理方法若有Map或者Model类型的入参,就会将请求内的隐含模型对象传递给这些入参,因此可以在方法体内对模型数据进行读写。推荐使用Model。

4. ModelAttribute

若希望将入参的数据对象放入数据模型中,就需要在相应入参前使用此注解。在以后的博文讲解,此处知道有这么个东西即可。

5. SessionAttribute

此注解可以将模型中的属性存入HttpSession中,以便在一次会话中共享该属性。


四. 视图解析器

请求处理方法执行完成之后,最终返回一个ModelAndView对象。对于那些返回String类型的处理方法,Spring MVC内部也会将它们装配成一个ModelAndView对象,它包含逻辑视图名和数据模型,那么接下来的工作就交给视图解析器(ViewResolver)了。

ViewResolver是Spring MVC处理视图的重要接口,通过它可以将控制器返回的逻辑视图名解析成一个真正的视图对象。Spring MVC默认提供了多种视图解析器,所有的视图解析器都实现了ViewResolver接口。

对于JSP这种常见的视图技术,通常使用InternalResourceViewResolver作为视图解析器。那么它是如何工作的?

InternalResourceViewResolver是最常用的视图解析器,通常用于查找JSP和JSTL等视图。通过源码就可以看出,它是URLBasedViewResolver的子类,会将返回的逻辑视图名都解析成InternalResourceViewResolver对象,该对象会把Controller的处理方法返回的模型属性都放在对应的请求作用域中,然后通过RequestDispatcher在服务器端把请求转发到目标URL。

我们只需要在配置文件中进行如下配置:

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/jsp/" />
    <property name="suffix" value=".jsp" />
bean>

总结

Spring MVC要记得概念比较多哦,所有我们必须要理解,如果不理解死记硬背是没什么用的。希望这篇文章可以帮助到大家。

你可能感兴趣的:(Spring MVC 体系结构和处理请求控制器)