SpringMVC:Struts2区别、搭建应用、流程处理、核心控制器

作为Spring框架的一部分,SpringMVC可以与Spring框架无缝整合。实际上,SpringMVC本身就是基于Spring核心容器的,SpringMVC的控制器天然就处于Spring容器的管理之下,因此Spring可以轻易地将Service组件注入控制器。

SpringMVC已逐渐取代Struts2的地位,成为Java领域最流行的MVC框架,这不仅仅由于它是Spring家族的产品,更由于SpringMVC本身更加简单、易用,而且功能非常强大。SpringMVC的控制器、处理方法都非常简单,通常只需要使用简单的注解修饰它们即可,它们无须实现特定的接口,也无须使用固定的方法签名,使用起来格外方便。

1,MVC概述

1.1,MVC模式极其优势

MVC将应用中的各组件按照功能进行分类,不同的组件使用不同的技术充当,甚至推荐了严格分层,不同的组件被严格限制在其所在的层内,各层之间以松耦合的方式组织在一起,从而提供了良好的封装。

MVC思想和优势:https://shao12138.blog.csdn.net/article/details/113877213#t2

1.2,SpringMVC概述

Spring MVC 是一款优秀的 Java Web 开发框架,具有以下几个好处:

  • 基于MVC的设计模式,将请求、处理和响应分离,让代码更加易于维护和扩展;
  • 支持多种视图技术,如JSP、Freemarker、Velocity等,可以灵活地选择适合自己的视图技术;
  • 易于进行测试和集成,能够轻松地进行针对控制器、业务逻辑和视图的单元测试;
  • 提供了强大的数据绑定、类型转换和校验功能,能够简化数据处理的编写;
  • 整合了Spring的IoC和AOP功能,提供了更加简洁和优雅的开发方式,也能够方便地与其他Spring组件进行整合。

总之,Spring MVC 能够帮助我们更快、更高效地开发 Web 应用程序,提高开发效率和代码质量。

SpringBoot和SpringMVC的区别:

  • 目的不同:Spring MVC是一个Web框架,用于开发Web应用程序;而Spring Boot是一个快速开发框架,用于快速创建和部署Spring应用程序。

  • 配置方式不同:Spring MVC需要手动配置各种组件,如数据源、事务管理、Web安全等;而Spring Boot提供了大量的自动配置,可以根据应用程序的classpath和配置信息,自动配置Spring应用程序所需的各种组件。

  • 依赖管理不同:Spring MVC需要手动管理各种依赖,如Servlet API、JSP API、JSTL等;而Spring Boot自动管理这些依赖,并提供了内嵌的Web服务器,可以快速启动Web应用程序。

  • 构建工具不同:Spring MVC可以使用任何构建工具,如Maven、Gradle等;而Spring Boot推荐使用Maven或Gradle来构建应用程序,并提供了一些特定的插件和依赖,如spring-boot-maven-plugin、spring-boot-starter-parent等。

  • 开发方式不同:Spring MVC需要手动编写大量的配置和代码,如XML配置、Controller、DAO等;而Spring Boot使用约定大于配置的开发方式,可以通过简单的注解和默认配置来快速开发Spring应用程序。

1.3,SpringMVC与Struts2的区别

目前Java领域两大主流的MVC框架就是SpringMVC和Struts 2,一般旧项目用Struts2比较多,但新项目往往用SpringMVC比较多,这说明SpringMVC正在蚕食Struts2的占有率,这也是SpringMVC的有点所致。总体来说,SpringMVC与Struts2存在以下几个区别:

  • 核心控制器区别:SpringMVC的核心控制器是Servlet(DispatcherServlet),而Struts2的核心控制器是Filter(StrutsPrepareAndExecuteFilter)。两者只是实现不同,并无优略之分。
  • 业务控制器区别:SpringMVC的业务控制器在容器中以单例模式应用,所有HTTP请求公用一个业务控制器(Controller)实例——不同的请求对应不同的方法;Struts2的业务控制器则不然,Struts2会针对每个HTTP请求都创建新的业务控制器(Action)实例。这一点,SpringMVC的性能优于Struts2。
  • 处理请求参数的区别:借助IoC容器的强大功能,SpringMVC直接通过方法形参来获取请求参数(只要让方法形参与HTTP请求参数同名即可);而Struts2则需要定义与请求参数同名的属性(实例变量+getter、setter方法)才能获取请求参数。这一点,SpringMVC完胜Struts2。从开发角度来看,SpringMVC代码简洁,Struts2代码臃肿;从性能角度来看,由于Struts2使用Action的实例变量来封装请求参数,这意味着Struts2必须为每个参数都创建不同的Action实例——否则就会存在线程安全问题;但SpringMVC的控制器可被设计成单例模式。
  • 请求参数支持上的区别:Struts2主要支持的依然是传统的POST、GET请求参数;但SpringMVC可以非常方便地支持各种请求参数,也支持URL路径中的请求参数、矩阵参数等,SpringMVC天生就对RESTful提供了良好支持。在这一点上,SpringMVC完胜Struts2。
  • 设计架构的区别:Struts2采用的是拦截器机制的设计,Struts2既提供了细粒度的拦截器,也允许将细粒度的拦截器组合成粗粒度拦截器栈;SpringMVC则采用了AOP机制设计。从本质上看,拦截器机制与AOP机制基本上殊途同归,二者并没有优略之分。
  • 文本响应区别:SpringMVC只需创建一个@ResponseBody注解,它就会自动处理方法返回的文本对象转换成JSON或XML文档响应,非常简洁;Struts2要么使用Stream二进制流来生成文本响应(非常烦琐),要么使用JSON插件将整个Action实力转换成JSON字符串来作为响应(相对比较简洁)——无论那种方式,SpringMVC都略胜一筹。
  • 配置文件区别:除非Struts2使用了Convention插件,否则Struts2应用必须为每个Action、每个视图映射都提供单独的配置;而SpringMVC除要在***-servlet.xml文件中做一些简单的配置之外,基本上是零配置,因此SpinrgmVC开发更加简洁。

2,SpringMVC入门(IDEA)

2.1,在Web应用中启动Spring容器

由于SpringMVC是一个针对Java Web的MVC框架,因此,首先需要建立一个动态的Java Web应用。步骤如下:

  • 创建Maven项目

  •  填写项目名称,其余默认或者自定义修改

  •  然后得到如下目录,创建一些其他目录

  • 在maven中引入jar包
<!--spring相关-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.9.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>5.2.9.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>5.2.9.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>5.2.9.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.9.RELEASE</version>
</dependency>
  • 在web.xml中配置创建Spring容器
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
    <!-- 配置Spring IoC配置文件路径 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationContext.xml</param-value>
    </context-param>
    <!-- 配置ContextLoaderListener用以初始化Spring IoC容器 -->
    <listener>
      <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
</web-app>
  • 创建applicationContext.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
</beans>

2.2,配置核心控制器

经过上面的配置,当Web应用启动时,在web.xml文件中配置的ContextLoaderListener会自动创建并初始化Spring容器。但是仅有Spring容器还不够,还必须在web.xml文件中配置Spring提供的核心控制器——该核心控制器负责拦截所有的HTTP请求。SpringMVC的核心控制器是DsipatcherServelt,它就是一个标准的Servlet,因此只要使用标准的Servlet配置语法配置它即可。

  • 在web.xml文件中增加如下配置片段配置DispatcherServlet
<servlet>
    <!-- 注意:Spring MVC框架会根据servlet-name配置,找到/WEB-INF/dispatcher-servlet.xml作为配置文件载入Web工程中 -->
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- 使得Dispatcher在服务器启动的时候就初始化 -->
    <load-on-startup>1</load-on-startup>
</servlet>
<!-- Servlet拦截配置 -->
<servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

<!--防止中文乱码-->
<filter>
    <filter-name>characterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
    <init-param>
        <param-name>forceEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>characterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

上面配置片段在Web应用中配置了DispatcherServlet,它主要完成两个功能:

  • 负责拦截HTTP请求,此处将它拦截的URL配置成“/”,这意味着它会拦截所有的请求。
  • 负责初始化另一个Spring容器。

当使用Spring与SpringMVC开发Web应用时,默认会存在两个Spring容器,其中ContextLoaderListener初始化的Spring容器是“根容器”,DispatcherServlet初始化的是“Servlet容器”。主要区别是:

  • ROOT容器主要负责配置、管理应用中的Service组件、DAO组件等后端组件。
  • Servlet容器主要负责配置、管理应用中的Controller组件、视图解析器、HandlerMapping组件等——主要管理MVC相关的组件,当程序在Servlet容器中找不到某个Bean时,Spring会自动到Root容器中继续查找。

DispatcherServlet初始化的Spring容器会自动部署默认的视图解析器、HandlerMapping等组件,ContextLoaderListenrer初始化的Spring容器则不会。如果只想初始化一个Spring容器,这是允许的,程序可去掉ContextLoadeListener的配置,这样Web应用启动时就不会初始化ROOT容器了。此外,也可让DispatcherServlet直接使用ContextLoaderListener加载Root容器,但不推荐只初始化一个Spring容器的方式,因为这种方式意味着控制器与后端容器混合成一个容器,不利于项目维护。

DispatcherServlet也要初始化Spring容器,其对应的配置文件在默认情况下,会以<servletname>-servlet.xml作为Spring容器的配置文件,其中<servletname>代表配置DispatcherServlet时指定的名字。比如上面配置片段中配置DispatcherServlet时指定的servlet-name为springmvc,这意味着它会以springmvc-servlet.xml作为Spring容器的配置文件。

  • 为了让该项目正常启动,在WEB-INF目录下增加一个springmvc-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:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
       http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
    <!-- 使用注解驱动 -->
    <mvc:annotation-driven />
    <!-- 定义扫描装载的包 -->
    <context:component-scan base-package="com.ysy.springmvc.Controller" />
</beans>

如果不想使用默认的<servletname>-servlet.xml作为Spring配置文件,也可以为DispatcherServlet配置一个contextConfigLocation参数,该参数用于为SpringMVC的Servlet容器指定配置文件。

<servlet>
    <!-- 注意:Spring MVC框架会根据servlet-name配置,找到/WEB-INF/dispatcher-servlet.xml作为配置文件载入Web工程中 -->
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- 使得Dispatcher在服务器启动的时候就初始化 -->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>WEB-INF/actionContext.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

至此,SpringMVC应用所需的两个Spring容器(Root容器和Servlet容器)都已配置完成,接下来即可为该引用开发控制器、Service组件了。

2.3,开发控制器

开发SpringMVC的控制器非常简单,只要留意以下三点:

  • 控制器类使用@Controller注解修饰。
  • 处理方法使用@RequestMapping注解修饰。@RequestMapping注解有@GetMapping、@PostMapping、@PutMapping、@DeleteMapping和@PatchMapping几个变体,它们的功能几乎相同,只不过后面几个变体注解专门处理GET、POST、PUT、DELETE、PATCH请求。
  • 处理方法为每个请求参数定义同名的形参。
@Controller
public class UserController {
    @Resource(name = "userService")
    private UserService userService;

    @PostMapping("/login")
    public String login(String username, String pass, Model model) {
        if (userService.userLogin(username,pass)>0){
            model.addAttribute("tip","欢迎您,登录成功!");
            return "/WEB-INF/content/success.jsp";
        }
        model.addAttribute("tip","对不起,您输入的用户名、密码不正确!");
        return "/WEB-INF/content/error.jsp";
    }
}

上面程序中定义了一个login()方法——该方法名是什么无关紧要,关键在于两点:

  • 该方法使用了@PostMapping("/login")修饰,因此,该方法会负责处理提交给/login的POST请求。
  • 该方法定义了username和pass两个形参,程序可以在该方法中通过这两个形参访问username、pass请求参数。

SpringMVC的控制器Bean是无状态的,它没有使用实例变量来封装请求参数,而是通过方法形参来获取请求参数的,这样的控制器就可以被设计成单例模式

login()方法还定义了一个Model类型的参数,Model其实就是一个类似于Map的接口(addAttribute()方法就类似于Map的put()方法),程序只要向该Model添加属性,这些属性自动传递给下一个处理结点(可能是视图页面也可能是处理方法)。

由于上面的控制器类已经使用了@Controller修饰,因此Spring会自动识别并在容器中配置该Bean——对于Spring容器来说,一切都是Bean,控制器也自然是Bean。另外还使用了@Resource修饰了userService成员变量,这表明Spring会将容器中id为userService的Bean赋值给该成员变量,userService是业务逻辑组件。

public class UserService {
    public Integer userLogin(String username, String pass) {
        if (username.equals("ysy")&&pass.equals("123456")){
            return 1;
        }
        return 0;
    }
}

由于上面的UserService并为使用@Service注解修饰,因此需要在applicationContext.xml文件中注入

<bean id="userService" class="com.ysy.springmvc.Service.UserService"/>

2.4,提供视图资源

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<html>
<body>
<h2>Hello World!</h2>
<form action="/login.do" method="post">
    <input type="text" name="username"/><br>
    <input type="password" name="pass"/><br>
    <input type="submit" value="登录"/>
</form>
</body>
</html>
--------------------------------------
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    ${tip}
</body>
</html>

3,SpringMVC的流程

3.1,SpringMVC应用的开发步骤

【步骤一】在web.xml文件中配置核心控制器DispatcherServlet处理所有的HTTP请求。由于Web应用是基于请求/响应架构的,所以不管使用哪种MVC Web框架,都需要在web.xml文件中配置该框架的核心Servlet或Filter。

<servlet>
    <!-- 注意:Spring MVC框架会根据servlet-name配置,找到/WEB-INF/dispatcher-servlet.xml作为配置文件载入Web工程中 -->
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- 使得Dispatcher在服务器启动的时候就初始化 -->
    <load-on-startup>1</load-on-startup>
</servlet>
<!-- Servlet拦截配置 -->
<servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>*.do</url-pattern>
</servlet-mapping>

【步骤二】如果需要以POST方式提交请求,则定义包含表单数据的视图页面,将该表单的action属性指定为请求提交的地址。

(1)如果是以GET方式发送请求,则无须经过这一步只需定义一个超链接,将该链接的href属性指定为请求提交的地址即可。

(2)如果以异步方式提交请求(比如jQuery、Vue等),则需要使用JS编写提交请求脚本。

【步骤三】定义处理请求的控制类,该类通常需要使用@Controller注解修饰。这一步是所有MVC框架中必不可少的,因为这个控制器类就是MVC中的C,它负责调用后端Service组件的方法来处理HTTP请求。

【问题】Controller并不能直接接收HTTP请求,为什么能处理请求HTTP请求呢?

MVC框架的底层机制是当核心控制器(DispatcherServlet)接收到HTTP请求后,通常会对该请求进行简单的预处理,例如解析、封装参数等,然后通过反射来创建Controller实例,并调用Controller的指定方法(由@RequestMapping注解或其变体修饰的方法)来处理请求。

【问题】当DispatcherServlet拦截HTTP请求后,它如何知道创建哪个控制器的实例呢?

(1)利用XML配置文件。比如Struts2或Spring2.5之前的SpringMVC框架,都要求在XML文件中配置/abc请求对应与调用哪个类的那个方法,这样就可以让MVC框架知道要创建那个控制器的实例了。

(2)利用注解。现在的SpringMVC只要使用@Controller修饰控制器类,并使用@RequestMapping或其变体修饰处理方法,即可让MVC框架知道创建哪个控制器的实例,并调用哪个方法来处理用户请求。

在SpringMVC框架中,控制器实际上由两部分组成,即:拦截所有HTTP请求和处理请求的通用代码都由核心控制器DispatcherServlet完成,而实际的业务控制(诸如调用Service组件的方法、返回处理结果等)则由自定义的Controller处理——因此,Comtroller也被称为业务控制器。

【步骤四】配置控制器类。也就是配置某个请求由哪个类的哪个方法负责处理。

现在的SpringMVC只要使用@Controller修饰控制器类,并使用@RequestMapping注解或其变体修饰处理方法即可。

@Controller
public class UserController {
    @Resource(name = "userService")
    private UserService userService;

    @PostMapping("/login")
    public String login(String username, String pass, Model model) {
        if (userService.userLogin(username,pass)>0){
            model.addAttribute("tip","欢迎您,登录成功!");
            return "/WEB-INF/content/success.jsp";
        }
        model.addAttribute("tip","对不起,您输入的用户名、密码不正确!");
        return "/WEB-INF/content/error.jsp";
    }
}

【步骤五】定义视图解析器。

SpringMVC控制器的出来方法总是返回一个String对象,该String对象通常并不是具体的视图资源,因此还需要视图解析器负责解析String对象与视图资源之间的对应关系。

SpringMVC还允许使用ModelAndView作为控制器处理方法的返回值(这也是早期SpringMVC遗留的问题),ModelAndView其实就是Model和View,其中Model用于封装传递给下一个处理结点的数据,而View(其实就是一个String对象)就相当于处理方法所返回的字符串。

【问题】为什么前面的示例中没有配置视图解析器呢?

这样是SpringMVC框架的优秀所在,当开发者没有配置任何视图解析器时,SpringMVC会提供默认的、最基础的视图解析机制。

一般来说程序没必要为每个控制器都配置单独的视图解析器,一个视图解析器通常可以为一批控制器提供视图功能。

【步骤六】提供视图资源。

当控制器处理用户请求结束后,通常返回一个String对象(或ModelAndView对象)作为逻辑视图名,视图解析器会根据该String对象解析到具体的物理视图资源,因此还需要提供对应的物理视图资源。

如果控制器需要将数据传递给视图资源,则通常建议在处理方法中增加一个Model类型的参数,并将这些数据添加成Model对象的属性;如果采用SpringMVC早期的风格,让处理方法返回ModelAndView对象,则也可以将这些数据添加成ModelAndView对象的属性。

3.2,SpringMVC的运行流程

SpringMVC的开发步骤实际上是按“请求——响应”的流程进行。

  • 用户通过浏览器向Web应用发送请求,该请求由部署在web.xml文件中的DispatcherServlet负责处理。 
  • 当DispatcherServlet接收到请求时,它会通过容器的一个或多个HandlerMapping对象来处理该URL。HandlerMapping对象也是部署在Spring容器中的Bean,它实现了HandlerMapping接口来获取对应的Handler。
  • HandlerMapping(HandlerMapping负责将请求的URL映射到对应的Handler)对象会根据URL返回对应的Handler对象。(Handler是对控制器中特定方法的包装,由于SpringMVC的Controller可以包含很多个处理方法,因此它的每个处理方法都能被包装成Handler
  • HandlerAdapter在准备调用Handler(控制器的特定方法)之前,会先对请求参数进行一定处理(通过HttpMessageConverter),主要包括:消息转换(JSON、XML等数据转换成对象,将对象转换成指定格式的响应数据等)、数据转换(将String转换成Integer、Double等)、数据校验(校验数据的有效性,将校验的结果存储到BindingResult或Error中)。
  • 当控制器处理完请求后,会将逻辑视图名和Model返回给DispatcherServlet。Model也包含了控制器要传递给视图进行显示的数据。
  • 当DispatcherServlet接收到视图名后,它会通过容器中的一个或多个ViewResolver(视图解析器)来处理该视图名。
  • 视图解析器会根据视图名返回视图对象。(视图解析器负责根据视图名确定对应视图)
  • 当DispatcherServlet确定了实际要呈现的视图之后,它就会将Model数据也传递给对应的视图。
  • 视图则负责呈现数据,并生成最终的响应。
  • 最后由DispatcherServlet将最终的响应呈现给用户。

SpringMVC的运行流程中涉及的核心组件:

  • DispatcherServlet:核心控制器,由Spring框架提供,开发者需要在web.xml文件中配置它。
  • HandlerMapping:负责将请求URL映射到对应的Handler。开发者可能可能需要选择、配置合适的HandlerMapping类。
  • Controller:控制器,开发者需要定义Controller类,并使用合适的注解修饰该类及其处理方法。
  • HandlerAdapter:它负责在调用Controller之前对请求参数进行一些前置处理,通常不需要开发者关心。
  • ViewResolver:视图解析器,它负责根据视图名确定对应的视图资源。开发者可能需要选择、配置合适的视图解析器。
  • View:视图资源。开发者需要编写视图页面。

3.3、核心控制器(DispatcherServlet)

DispatcherServlet源代码包括如下片段:

protected void initStrategies(ApplicationContext context) {
    //初始化文件上传解析器
    this.initMultipartResolver(context);
    //初始化国际化解析器
    this.initLocaleResolver(context);
    //初始化主题解析器
    this.initThemeResolver(context);
    //初始化HandlerMapping对象
    this.initHandlerMappings(context);
    //初始化HandlerAdapters对象
    this.initHandlerAdapters(context);
    //初始化处理器异常解析器
    this.initHandlerExceptionResolvers(context);
    //初始化视图名解析器
    this.initRequestToViewNameTranslator(context);
    //初始化视图解析器
    this.initViewResolvers(context);
    //初始化FlaashMap管理器
    this.initFlashMapManager(context);
}

initStragies()方法会在WebApplicationContext初始化后自动执行,它会自动扫描Spring容器中所有的Bean,根据它们的名称或类型来查找上面这些特殊的Bean,如果没找到,DispatcherServlet会为这些特殊的Bean装配到一起。

特殊的Bean解释
HandlerMapping将请求URL映射到对应的Handler,目前的主流实现是基于注解的映射实现。
HandlerAdapter主要用于帮助DispatcherServlet调用控制器(Adapter),其主要作用就是对DispatcherServlet屏蔽控制器的各种细节。
HandlerExceptionResolver主要作用就是将异常映射到视图。
ViewResolver将逻辑视图名(String对象)解析到实际视图(View对象)。

LocaleResovler

LocaleContextResolver

用于解析客户端正在使用的语言、区域设置及所在的时区,以便应用呈现国际化界面。
ThemeResolver解析Web应用程序可以使用的主体,例如,提供个性化布局。
MultipartResolver解析multi-part请求,主要用于支持对HTML表单的文件上传。
FlashMapManager负责存储和检索从一个请求传递到另一个请求的“input”和“output”属性。

在spring-webmvc-<版本号>.RELEASE.jar文件的org\springframework\web\servlet路径下有一个DispatcherServlet.properties配置文件,该文件指定了这些特殊的Bean的默认实现:

# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.
# 定义默认的国际化解析器实现类
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
# 定义默认的主题解析器实现类
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
# 定义默认的HandlerMapping解析器实现类(2个)
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
	org.springframework.web.servlet.function.support.RouterFunctionMapping
# 定义默认的HandlerAdapter解析器实现类(3个)
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
	org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
	org.springframework.web.servlet.function.support.HandlerFunctionAdapter
# 定义默认的处理器异常解析器实现类(2个)
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
	org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
	org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
# 定义默认的视图名解析器实现类
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
# 定义默认的视图解析器实现类
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
# 定义默认的FlashMap管理器实现类
org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

这些特殊的Bean有些只允许有一个实例,比如文件上传解析器MultipartResolver、国际化解析器LocaleResovler等;有些允许有多个实例,如HandlerMapping、处理器适配器HandlerAdapter等。

如果存在多个同一类型的组件,那么如果确定它们的优先级呢?由于这些组件都实现了org.springframework.core.Ordered接口,因此可以通过它们重写getOrder()方法的返回值来确定优先级,getOrder()方法的返回值越小优先级越高。

如果将DispatcherServlet的配置文件设置为null,DispatcherServlet将不会使用单独的容器,而是直接使用Root容器作为Servlet,此时整个应用只有一个Spring容器。

下面web.xml配置文件示范了这种配置:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
  <!-- 配置Spring IoC配置文件路径 -->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/applicationContext.xml</param-value>
  </context-param>
  <!-- 配置ContextLoaderListener用以初始化Spring IoC容器 -->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <!-- 配置DispatcherServlet -->
  <servlet>
    <!-- 注意:Spring MVC框架会根据servlet-name配置,找到/WEB-INF/dispatcher-servlet.xml作为配置文件载入Web工程中 -->
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- 使得Dispatcher在服务器启动的时候就初始化 -->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value></param-value>
    </init-param>
  </servlet>
  <!-- Servlet拦截配置 -->
  <servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>
</web-app>

上面将contextConfigLocation参数设置为null,这样SpringMVC将直接使用Root容器作为Servlet容器,因此需要在Root容器对应的配置文件中进行SpringMVC相关配置。

<?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:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
       http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
    <!-- 使用注解驱动 -->
    <mvc:annotation-driven />
    <!-- 定义扫描装载的包 -->
    <context:component-scan base-package="com.ysy.springmvc.Controller" />
    <bean id="userService" class="com.ysy.springmvc.Service.UserService"/>
</beans>

3.4,<mvc:annotation-driven />

<mvc:annotation-driven />(注解驱动)元素是为容器开启SpringMVC功能的快捷配置,如果为DispatcherServlet提供了单独的配置文件,则在该文件中添加该元素;如果DispatcherServlet没有单独的配置文件,则直接在Root容器的配置文件中添加该元素。

<mvc:annotation-driven />在容器中配置了如下三个特殊的Bean:

  • andlerMapping:使用RequestMappingHandlerMapping作为该特殊的Bean的实现类。
  • HandlerAdapter:使用RequestMappingHandlerAdapter作为该特殊的Bean的实现类。
  • HandlerExceptionResolver:使用ExceptionHandlerExceptionResolver作为该特殊的实现类。

此外,它除支持用于数据绑定的PropertyEditor之外,还通过ConversionService实例提供了Spring 3风格的类型转换功能:

  • 借助ConversionService的支持,可以使用@NumberFormat注解对Number类型的字段进行格式化。
  • 借助ConversionService的支持,可以使用@DateTimeFormat注解对Date、Calendar、Long和Joda Time类型的字段进行格式化。
  • 如果应用本身支持JSR 303校验机制,则可对控制器通过@Valid执行输入校验。
  • 注册一些列HttpMessaageConverter,支持对@RequestBody注解修饰的方法参数和@ResponseBody注解修饰的方法返回值进行转换。

HttpMessageConverter组件的主要有如下两个作用:

  • 将HTTP请求数据(如JSON或XML数据)转换成符合控制器的处理方法要求的参数。
  • 当控制器的处理方法处理结束后,该方法的返回值转换成JSON或XML数据。

HttpMessageConverter只是一个接口,Spring为之提供了大量的实现类,<mvc:annotation-driven />设置了如下HttpMessageConverter实现类的实例:

  • ByteArrayHttpMessageConverter:用于处理字节数组数据的转换。
  • StringHttpMessageConverter:用于处理字符串数据的转换。
  • ResourceHttpMessageConverter:完成目标类型与javax.xml.transform.Source类型之间的转换。
  • FormHttpMessageConverter:完成表单数据与MultiValueMap<String,String>对象之间的转换。
  • Jaxb2RootElementHttpMessageConverter:完成Java对象与XML消息之间的转换。

MappingJackson2XMLHttpMessageConverter:完成Java对象与XML消息之间的转换。当类路径下存在Jackson XML2时才注册该转换器。

  • MappingJackson2HttpMessageConverter:完成Java对象与JSON数据之间的转换。当类加载路径下存在Jackson2时才注册该转换器。
  • AtomFeedHttpMessageConverter:完成Atom feed与Java对象之间的转换。当类加载路径下存在Rome时才注册该转换器。
  • RssChannelHttpMessageConverter:完成RSS feed与Java对象之间的转换。当类加载路径下存在Rome时才注册该转换器。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值