SpringMVC基础应用

100 阅读23分钟

一.SpringMVC框架整理学习

1.SpringMVC-框架概述

📕 什么是SpringMVC ?

  • SpringMVC是Spring的子模块,为展现层提供的基于MVC设计理念的优秀的web框架,是目前主流的MVC框架之一。
  • SpringMVC通过一套MVC注解,让POJO成为处理请求的控制器,而无须实现任何接口。
  • 支持REST风格的URL请求
  • 采用了松散耦合可拔插的组件结构,比其他MVC框架更具扩展性和灵活性。
  • SpringMVC是Servlet的轻量级封装,在控制层使用控制器Controller代理传统的JavaWeb开发中的Servlet或Struts框架中的Action

📕 什么是MVC ?

  1. MVC是模型(Model)、视图(View)、控制器(Controller)的简写,是一种软件设计规范。
  2. 将业务逻辑、数据、显示分离的方法来组织代码。降低了视图和业务逻辑的双向耦合。
  3. MVC是一种架构模式,不同的MVC存在不同的差异。

2. SpringMVC-搭建开发环境

🎯 创建基于Maven的JavaWeb项目TestSpringMVC

🎯 在pom文件中添加jar包的依赖

<!-- Spring Context Spring核心 -->
<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.0.2.RELEASE</version>
</dependency>
<!-- Spring Web 包含eb应用开发时,用到的Spring框架时所需的核心类 -->
<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>5.0.2.RELEASE</version>
</dependency>
<!-- Spring MVC 实现SpringMVC的操作-->
<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.0.2.RELEASE</version>
</dependency>
<!-- Servlet API 对于Servlet的开发环境支持 -->
<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>javax.servlet-api</artifactId>
  <version>3.1.0</version>
  <scope>provided</scope>
</dependency>
<!-- JSP API  Jsp的支持-->
<dependency>
  <groupId>javax.servlet.jsp</groupId>
  <artifactId>jsp-api</artifactId>
  <version>2.0</version>
  <scope>provided</scope>
</dependency>
<!-- JSTL JSP标签库-->
<dependency>
  <groupId>jstl</groupId>
  <artifactId>jstl</artifactId>
  <version>1.2</version>
</dependency>

🎯 在web.xml中添加SpringMVC的前端控制器的配置

<!--SpringMVC的前端控制器,用来处理全部的HTTP请求-->
<servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <!--指定配置文件的位置和名称,classpath:表示从项目的类路径开始开始-->
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:springmvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

【注意】如果不指定contextConfigLocation,则配置文件名称默认为servlet名称-servlet.xml,而且文件位置必须在WEB-INF文件夹下

🎯 在web.xml中添加POST请求中文乱码过滤器

SpringMVC内置了一个处理POST请求中文乱码的过滤器CharacterEncodingFilter

【位置】spring-web.jar中的org.springframework.web.filter包中

<!--POST请求中文乱码处理过滤器-->
<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>
</filter>
<filter-mapping>
    <filter-name>CharacterEncodingfilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

🎯 创建springmvc.xml的配置文件​添加context和mvc的名称空间,以及注解驱动的相关配置

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"  
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    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.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.wisedu.controller"/>
    <!--处理器映射器
    <bean class="org.springframewok.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    处理器适配器
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
    
    -->
    <!--视图渲染器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
    <!--开启MVC注解扫描 ,一般来说我们只需要配置视图解析器,通常,我们只需要手动配置视图解析器,      而处理器映射器和处理器适配器只需要开启注解驱动即可-->
    <mvc:annotation-driven/>
    
</beans> 

🎯 mvc:annotation-driven/标签作用

  • 开启注解驱动

    ( 1 )自动向SpringIOC容器中注册RequestMappingHandlerMapping类的Bean和RequestMappingHandlerAdapter类的Bean

    ( 2 )HandlerMapping接口的实现类RequestMappingHandlerMapping处理url请求的映射器,解析@RequestMapping注解,并将其注册到请求映射表中

    ( 3 )HandlerAdapter接口的实现类RequestMappingHandlerAdapter处理请求的适配器,确定调用哪个处理器的哪个方法

  • 其他功能

    ( 1 )支持使用ConversionService实例对表单参数进行类型转换

    ( 2 )支持使用@NumberFormat注解和@DataTimeFormat注解完成数据类型的格式化

    ( 3 )支持使用@Valid注解进行JSR303数据校验

    ( 4 )支持使用@RequestBody和@ResponseBody注解处理JSON数据

    ( 5 )自动注册JSON数据解析的适配器HttpMessageConverter

2.1 @RequestBody和@ResponseBody详解

@RequestBody

作用:

i) 该注解用于读取Request请求的body部分数据,使用系统默认配置的HttpMessageConverter进行解析,然后把相应的数据绑定到要返回的对象上;

ii) 再把HttpMessageConverter返回的对象数据绑定到 controller中方法的参数上。

使用时机:

A) GET、POST方式提时, 根据request header Content-Type的值来判断:

  • application/x-www-form-urlencoded, 可选(即非必须,因为这种情况的数据@RequestParam, @ModelAttribute也可以处理,当然@RequestBody也能处理);
  • multipart/form-data, 不能处理(即使用@RequestBody不能处理这种格式的数据);
  • 其他格式, 必须(其他格式包括application/json, application/xml等。这些格式的数据,必须使用@RequestBody来处理);

B) PUT方式提交时, 根据request header Content-Type的值来判断:

  • application/x-www-form-urlencoded, 必须;
  • multipart/form-data, 不能处理;
  • 其他格式, 必须;

说明:request的body部分的数据编码格式由header部分的Content-Type指定;


@ResponseBody

@responseBody注解的作用是将controller的方法返回的对象通过适当的HttpMessageConverter转换为指定的格式之后,写入到response对象的body区,通常用来返回JSON数据或者是XML

数据,需要注意的呢,在使用此注解之后不会再走试图处理器,而是直接将数据写入到输入流中,他的效果等同于通过response对象输出指定格式的数据。

💨如果返回的类型为String,会造成乱码。

【原因】

不同的数据类型,前后端交互的时候通过指定的HttpMessageConverter转换格式,String数据类型对应的HttpMessageConverter的默认编码格式为 ISO-8859-1【解决方案第二节HttpMessageConverter中解决】

2.2 HttpMessageConverter 消息转换器

@RequestBody、@ResponseBody注解,可以直接将输入解析成Json、将输出解析成Json,但HTTP 请求和响应是基于文本的,意味着浏览器和服务器通过交换原始文本进行通信,而这里其实就是HttpMessageConverter发挥着作用。

Http请求响应报文其实都是字符串,当请求报文到java程序会被封装为一个ServletInputStream流,开发人员再读取报文,响应报文则通过ServletOutputStream流,来输出响应报文。

从流中只能读取到原始的字符串报文,同样输出流也是。那么在报文到达SpringMVC / SpringBoot和从SpringMVC / SpringBoot出去,都存在一个字符串到java对象的转化问题。这一过程,在SpringMVC / SpringBoot中,是通过HttpMessageConverter来解决的。HttpMessageConverter接口源码:

public interface HttpMessageConverter<T> {
 //可读
  boolean canRead(Class<?> clazz, MediaType mediaType);
 //可写
  boolean canWrite(Class<?> clazz, MediaType mediaType);
 //获取支持的媒体类型
  List<MediaType> getSupportedMediaTypes();
 
  T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
      throws IOException, HttpMessageNotReadableException;
 
  void write(T t, MediaType contentType, HttpOutputMessage outputMessage)
      throws IOException, HttpMessageNotWritableException;
}

下面以一例子来说明:

@RequestMapping("/test")
@ResponseBody
public String test(@RequestBody String param) {
  return "param '" + param + "'";
}

在请求进入test方法前,会根据@RequestBody注解选择对应的HttpMessageConverter实现类来将请求参数解析到param变量中,因为这里的参数是String类型的,所以这里是使用了StringHttpMessageConverter类,它的canRead()方法返回true,然后read()方法会从请求中读出请求参数,绑定到test()方法的param变量中。

同理当执行test方法后,由于返回值标识了@ResponseBody,SpringMVC / SpringBoot将使用StringHttpMessageConverter的write()方法,将结果作为String值写入响应报文,当然,此时canWrite()方法返回true。

HttpMessageConverters的实现类:

image-20201228142416745

StringHttpMessageConverter问题场景:

 <!--  POST请求编码过滤器        -->
        <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.xml中设置了POST请求过滤器以后,但是Controller中返回类型为String字符串转变为JSON格式,此时会出现???乱码

通过源码分析:

public class StringHttpMessageConverter extends AbstractHttpMessageConverter<String> {
    public static final Charset DEFAULT_CHARSET = Charset.forName("ISO-8859-1");

String字符串转JSON格式使用的转换器是StringHttpMessageConverters

默认使用的编码及是IOS--8859-1;所以出现乱码格式。我们可以设置里面的属性supportedMediaTypes来修改编码格式:

<mvc:message-converters>
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                <property name="supportedMediaTypes">
                    <list>
                        <value>text/plain;charset=UTF-8</value>
                    </list>
                </property>
            </bean>
</mvc:message-converters>

2.3 DispatcherServlet的执行原理

(1)  DispatcherServlet表示前端控制器,是整个SpringMVC的控制中心。用户发起请求,DispatcherServlet都会接收并拦截请求
    ·我们假设请求url地址为:http://localhost:8080/SpringMVC/hello
    ·如上url拆分成三个部分:
    ·http://localhost:8080为服务器域名
    ·SpringMVC部署在服务器上的web站点
    ·hello表示控制器
    ·通过分析,如上url表示为:请求位于localhost:8080上的SpringMVC站点的hello控制器
(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) 最终视图呈现给用户。

2.4 映射请求路径

📕​ @RequestMapping注解

使用@RequestMapping注解映射请求url,根据url查找匹配的控制器及其内部的业务方法

  1. value属性

    【作用】用于对url进行分类管理,以免出现url冲突,url的完整格式为"/项目名/模块名/方法名"

    ( 1 ) @RequestMapping用在类上时对应的业务的模块名,提供初步的请求映射,即所有请求方法都以该地址作为父路径

    ( 2 ) @RequestMapping用在方法上时对应具体的业务方法,提供更加具有(窄化)的请求映射,即所有的请求方法是以该地址作为子路径

    注意 : 如果类上没有标注@RequestMapping注解,只是在方法上使用@RequestMapping,则方法上的url相对于项目的根路径

    //写法1
    @RequestMapping("/user")  //@RequestMapping用在类上,value属性写模块的名称
    public class UserController {
    ​
        @RequestMapping("/login")   //@RequestMapping用在方法上,value属性写具体操作的名称
        public void login(){
            System.out.println("login()...");
        }
        
        @RequestMapping("/reg")  
        public void reg(){
            System.out.println("reg()...");
        }   
    }
    ​
    //写法2
    public class UserController {
    ​
        @RequestMapping("/user/login")   //只在方法上使用@RequestMapping注解,value属性写完整路径  /模块名/方法名
        public void login(){
             System.out.println("login()...");
        }
        
        @RequestMapping("/user/reg")  
        public void reg(){
            System.out.println("reg()...");
        }   
    }
    
  2. method属性 ---限定HTTP的请求方式类型

    @RequestMapping(method={RequestMethod.POST, RequestMethod.GET})

    123

  3. Params属性 --- 限定访问url中必须包含的参数名和参数值 了解

    @RequestMapping(value="/login", params={"username", "password!=123"})

  4. @RequestMapping注解支持带有通配符的Ant风格的Url 了解

    ( 1 ) ? --- 匹配路径或文件名中的任意一个字符

    ( 2 ) * --- 匹配路径或文件名中的任意多个字符

    ( 3 ) **---匹配任意多层路径

📕 @GetMapping注解和@PostMapping注解

  1. @GetMapping注解---专门用来处理Get请求

    image-20210129133846778

    @GetMapping("url地址") 相当于@RequestMapping(value="url地址",method=RequestMethod.GET)

  2. @PostMapping注解---专门用来处理Post请求

    image-20210129133815872

    @PostMapping("url地址")相当于@RequestMapping(value="url地址",mehtod=RequestMethod.Post)

3. SpringMVC-控制器的数据输入

3.1 绑定简单的数据类型---基本数据类型+String

  • 方式一 : 使用request对象获取请求参数值

    在方法中添加类型为HttpServletRequest参数,SpringMVC会自动将request对象注入到方法中

    在业务方法中调用request对象的getParameter()方法获取请求参数的值

  • 方式二 : 使用@RequestParam注解获取请求参数值

    SpringMVC默认支持简单数据类型绑定,只需要在业务方法中添加对应的形参即可。

    【注意】方法的形参名称要和请求参数名保持一致,否则在形参上添加@RequestParam注解,并在注解的value属性中指定请求参数名

    ( 1 ) 在方法参数中定义和请求参数同名的方法参数,SpringMVC会自动将请求参数值注入到方法形参中。

    ( 2 ) 如果请求参数名和方法形参名不一致时,使用@RequestParam注解,并在注解的name属性中指定请求参数名【相当于HttpServletRequest接口的getParameter()方法】

  • @RequestParam注解

    ( 1 ) name或value属性---指定请求参数的名称

    ( 2 ) required属性--- 指定参数是否必须,默认为true,表示请求参数中必须包含对应的参数,如果参数不存在,则报错--- Required parameter 'xxx' is not present

    ( 3 ) defaultValue属性 --- 为请求参数提供默认值

  • @PathVariable注解

    ( 1 ) Restful风格的请求url --- 将参数直接写在路径中,而无需?携带

    ( 2 ) 带占位符的url是Spring3.0新增的功能,该功能是SpringMVC支持REST编程风格的重要基础

    @RequestMapping 直接中的url使用{}括起参数名

    @PathVariable将请求url中的{}占位符参数绑定到控制业务方法的形参中

    @RequestMapping("/delete/{userId}")

    public String pathVariable(@PathVariable("userId") String userId){

    }

    【注意】@RequestMapping中的占位符{}中的名称要和@PathVariable注解中的value属性值保持一致,与方法参数名无关,该注解取出url中占位符的数据并自动注入到方法形参中

3.2 绑定原生Servlet API对象

  • 如果需要在控制器的方法中使用原生的Servlet API对象,只需直接在业务方法中添加对应的形参即可。

  • SpringMVC支持以下类型对象的自动注入

    HttpServletRequest 常用

    HttpServletResponse 常用

    HttpSession 常用

    InputStream和OutputStream

    Reader和Writer

3.3 绑定POJO

SpringMVC支持POJO的自动注入,只需直接在业务方法中添加对应的POJO的类型的参数即可,并支持级联属性的注入

【工作原理】 SpringMVC根据请求参数名和POJO类的属性名自动匹配,因此要求请求参数名和POJO对象的属性名保持一致,否则无法注入成功。

3.4 绑定数组

SpringMVC支持POJO的自动注入,只需直接在业务方法中添加对应的数组类型的参数即可。

【工作原理】

( 1 ) 自动调用getParameterValues()方法获取到数组参数值,并注入到数组类型的同名形参中

( 2 ) 如果形参声明为String类型,还能自动将数组元素拼接成一个以逗号分隔的字符串

( 3 ) 如果形参声明为非String类型的数组,还能自动进行类型转换。

3.5 数据绑定的工作原理

📕 SpringMVC通过JDK动态代理或CGLIB技术为控制器类创建一个代理对象

📕 在代理对象中,通过HttpServletRequest接口的获取提交的参数名和参数值

getParameterNames()方法返回所有请求参数的名称

getParameterValues()方法返回指定参数名对应的所有参数值

getParameterMap()方法返回参数名作为key,参数值作为value的Map

📕 根据获取到的String类型的参数,通过各种类型转换器(Converter)将参数自动转换为对应的数据类型,同时允许程序员自定义类型转换器并添加到类型转换器集合中

📕 通过反射调用控制器的业务方法,将转换好的数据注入到形参变量中,这样业务方法就能实现自动接收数据的效果了

【总结】 底层通过动态代理技术将获取表单参数,类型转换、数据校验等代码动态织入到控制器的业务方法中。

3.6 从属性文件中读取初始化参数

( 1 ) 创建属性文件config.properties,存放在resources文件夹下

loginScope=2

( 2 ) 使用<context:property-placeholder >标签加载属性文件

<context:property-placeholder location="classpath:config.properties"/>

【注意】此标签只能在配置文件中使用一次,如果项目存在多个属性文件时,可以使用通配符*

( 3 ) 在控制器类中声明成员属性,并使用@Value注解获取属性值

@Value("${loginScope}")
private int loginScope;

3.7自定义类型转换器

🌳 查看spring-core.jar文件中内置的转换器类

(1)org.springframework.core.convert.converter包下的Converter接口

(2)org.springframework.core.convert.support包下的各种转换器的源码---StringToNumberConverterFactory类 StringToArrayConverter类

(3)org.springframework.context.support包下的ConversionServiceFactoryBean类,其中有一个converters集合属性


Spring MVC上下文中内置了大量的类型转换器,实现不同数据类型之间的转换,可以完成大部分的类型转换工作


java.lang.Boolean -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter

java.lang.Character -> java.lang.Number : CharacterToNumberFactory

java.lang.Character -> java.lang.String : ObjectToStringConverter

java.lang.Enum -> java.lang.String : EnumToStringConverter

java.lang.Number -> java.lang.Character : NumberToCharacterConverter

java.lang.Number -> java.lang.Number : NumberToNumberConverterFactory

java.lang.Number -> java.lang.String : ObjectToStringConverter

java.lang.String -> java.lang.Boolean : StringToBooleanConverter

java.lang.String -> java.lang.Character : StringToCharacterConverter

java.lang.String -> java.lang.Enum : StringToEnumConverterFactory

java.lang.String -> java.lang.Number : StringToNumberConverterFactory

java.lang.String -> java.util.Locale : StringToLocaleConverter

java.lang.String -> java.util.Properties : StringToPropertiesConverter

java.lang.String -> java.util.UUID : StringToUUIDConverter

java.util.Locale -> java.lang.String : ObjectToStringConverter

java.util.Properties -> java.lang.String : PropertiesToStringConverter

java.util.UUID -> java.lang.String : ObjectToStringConverter

🌳 类型转换器的工作原理

(1)ConversionService是类型转换体系的核心接口,可以利用ConversionServiceFactoryBean在IoC容器中定义一个ConversionService

    Spring MVC会自动识别出IoC容器中的转换服务ConversionService,并在Bean属性配置和业务方法形参绑定时使用ConversionService进行数据的转换

(2)为了满足项目的业务需求,程序员可以提供自定义的类型转换器类,并通过ConversionServiceFactoryBean的converters属性注册自定义的类型转换器,将其加入到转换器集合中

🌳 自定义的类型转换器的开发步骤

(1)创建自定义的类型转换器,实现Converter接口,指定泛型参数,并重写convert()方法实现转换逻辑

(2)在mvc:annotation-driven标签中指定类型转换服务,并注册自定义的类型转换器类

<!-- 开启注解驱动,同时指定类型转换服务的bean名称 -->
<mvc:annotation-driven conversion-service="conversionService"/>
    
<!-- 配置类型转换服务,注册自定义的类型转换器 -->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"><property name="converters">
        <set>
        <bean class="自定义类型转换器的完整类名"/>
        </set>
    </property></bean>

4. SpringMVC-控制器的数据输出

4.1 控制器方法的三种返回类型

  1. 返回void类型

    不使用处理器映射器进行页面跳转,直接在控制器中使用response对象完成响应输出此方法一般用于验证码、Ajax、文件下载等业务场景

  2. 返回String类型

    直接返回要跳转的视图页面文件名,默认是请求转发,重定向要加上redirect:前缀

  3. 返回ModelAndView类型

    调用setViewName()方法,将跳转视图的信息封装到ModelAndView对象中。

4.2 处理模型数据的四种方法

  1. 方法返回Stirng时

    如果控制器的业务方法返回String类型时,可以使用以下四种方式向request属性范围保存数据:

    ( 1 ) 添加Mdeol类型的输入参数,并调用addAttribute()方法,填充模型数据

    ( 2 ) 添加Map类型的输入参数,并调用put()方法,填充模型数据

    ( 3 ) 添加ModelMap类型的输入参数,并调用put()或addAttribute()方法,填充模型数据

    ( 4 ) 在方法形参中添加@ModelAttribute注解,同时指定value属性为请求参数名,则此参数会自动填充到模型数据中

    【注意】重定向操作无法使用以上四种方法保存数据,必须将数据保存在session属性范围中。

  2. 方法返回ModelAndView类型时

    如果控制器的业务方法返回ModelAndView类型,可以调用addObject()方法,填充模型数据

4.3 配置视图解析器

  1. 视图解析器 --- ViewResolver类

    配置InternalResourceViewResolver视图解析器,可以简化视图名称的写法,该类在webmvc这个jar包

    <!--配置视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--指定页面所在的文件,即视图的前缀-->
        <property name="prefix" value="/WEB-INF/"></property>
        <!--指定页面的扩展名,即视图的后缀-->
        <property name="suffix" value=".jsp"></property>
    </bean>
    

    (1)为了提高系统的安全性,往往将JSP页面放到项目的WEB-INF文件夹中,这样客户端就 无法直接访问这些页面

    (2)使用以上配置后,控制器业务方法返回的是逻辑视图名(仅仅是页面的主文件名), InternalResourceViewResolver负责在获取到的逻辑视图名的前面和后面拼接上前缀 prefix和后缀suffix,最终形成完整的路径

    【注意】此配置只针对服务器请求转发操作有效

  2. 视图 --- View类

    ( 1 ) 所有视图类都继承了AbstractView类,该类实现了View接口,并重写了render()方法实现视图的渲染【缺省适配器模式】

    ( 2 ) AbstractView类的render()方法中调用了renderMergedOutputModel()方法,该方法是抽象方法,等待具体的视图子类重写【体现模板方法设计模式】

    ( 3 ) IntermalResourceView类是AbstractView类的具体子类,renderMergedOutModel()方法中封装了原始的request.getRequestDispatcher().formard()方法完成服务器请求转发

    ( 4 ) RedirectView类是AbstractView类的具体子类,renderMergedOutputModel()方法中封装了原始的response.sendRedirect()方法返回客户端重定向

4.4 处理静态资源请求的三种方式

📕 问题产生的原因

如果前端控制器DispatcherServlet中的url-pattern设置为/,Spring MVC会拦截全部的请求,其中 也包含对静态资源(如图片,js文件,css文件)的请求, Spring MVC会将其当成一个普通请求处理, 因为找 不到对应的处理器,所以导致404错误。

📕 解决方案

【方案1】修改web.xml,将前端控制器DispatcherServlet中的url-pattern设置为 .do或.action

表示只拦截.do后缀的url,此方式需要修改项目中所有请求路径,比较麻烦,不推荐使用

【方案2】修改Spring MVC的配置文件---添加mvc:resources标签处理静态资源请求

<mvc:resources mapping="/image/**" location="/image/"/>

<mvc:resources mapping="/css/**" location="/css/"/>

<mvc:resources mapping="/js/**" location="/js/"/>

【方案3】修改Spring MVC的配置文件---添加mvc:default-servlet-handler/标签处理静态资源请求

<!-- 处理静态资源 -->
<mvc:default-servlet-handler />

【工作原理】

<mvc:default-servlet-handler>标签用于指定对项目中的静态资源的处理器DefaultServletHttpRequestHandler类会对进入前端控制器DispatcherServlet的请求进行过滤, 如果发现请求的是静态资源(image,css,js,html等)无法匹配处理器映射器, 就将该请求交由Web容器中默认的Servlet处理(Tomcat中是DefaultServlet),只有非静态资源才交给前端控制器DispatcherServlet继续处理

5. SpringMVC-文件的上传和下载

5.1 文件的上传

  1. 添加上传文件所需的jar包依赖

    SpringMVC内置了commons-fileupload作为文件上传组件,该依赖自动导入commons-id组件

  2. 在springmvc.xml文件中配置文件上传的解析器,id属性必须为multipartResolver

    <!-- 文件上传解析器 -->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
         <property name="maxUploadSize" value="所有上传文件的最大字节数总和" />*
         <property name="maxUploadSizePerFile" value="单个上传文件的最大字节数" />
         <property name="defaultEncoding" value="默认编码" />
         <property name="maxInMemorySize" value="内存中的最大值" />
    </bean>
    
  3. 文件上传的三个条件

    ( 1 ) 标签必须指定method属性为post

    ( 2 )标签必须指定entype属性为multipary/form-data,表示以二进制流的形式传输文件内容

    ( 3 )使用类型为file的标签,供用户选择文件

  4. 在业务方法中添加MultipartFile类型的形参,调用transferTo()方法

    (1)org.springframework.web.multipart.MultipartFile类封装了上传文件对应的二进制信息

    (2)为了防止同名文件的覆盖,保存文件前需要将主文件名进行改名处理

    (3)文件保存在项目中的指定目录,使用servletContext接口的getReaPath()方法返回真实的物理路径

  5. MultipartFile类的API

    byte[] getBytes() //获取文件内容对应的字节数组

    String getContentType() //获取文件的内容类型

    InputStream getInputStream() //返回文件内容对应的输入流

    String getName() //返回临时文件名

    String getOriginalFilename() //返回原始文件名 常用

    long getSize() //返回文件大小 常用

    boolean isEmpty() //判断文件是否为空 常用

    void transferTo(File dest) //将临时文件保存到指定位置 常用

  6. 多文件上传

    多文件上传时,业务方法的参数类型是MultipartFile的数组,通过循环遍历数组的每一个MultipartFile对象进行上传操作

5.2 文件的下载

1. 业务方法的返回类型为void

2. 通过request对象获取下载资源对应的字节输入流对象

3. 通过response对象获取字节输出流对象

4. 添加名为Content-Disposition的响应头,设置文件的处理方式为下载

5. 使用拷贝字节流或使用IOUtils类提供的copy()方法完成文件的传输

    @RequestMapping("/download")
    public void download(String filename, HttpServletRequest request, HttpServletResponse response) throws Exception {
         
         //构造字节输入流
         InputStream is = request.getServletContext().getResourceAsStream("/download/" + filename);
​
         //获取字节输出流 
          OutputStream os = response.getOutputStream();
         
         //添加响应头,设置文件的处理方式为下载
         response.addHeader("Content-Disposition", "attachment;filename=" + filename);  //设置下载时的文件名
​
         //拷贝字节流---方式1
         byte[] buffer = new byte[1024];
             
         BufferedInputStream bis = new BufferedInputStream(is);
             
         int length = 0;
​
         while ((length = bis.read(buffer))!= -1) {
              os.write(buffer, 0, length);
         }
​
         //拷贝字节流---方式2
          //IOUtils.copy(is, os);
       
         bis.close();
          os.close();
     }

6. SpringMVC-处理JSON数据

SpringMVC内置了Jackson作为JSON数据的解析器

  1. 在pom.xml文件中导入Jackson数据解析的jar包依赖

     <!-- Jackson JSON数据解析 -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.9.8</version>
    </dependency>
    

    该Jar包自动导入了jackson-annotation.jar、jackson-core.jar、jackson-databind.jar这三个jar包

  2. 确保在springmvc.xml中添加了mvc:annotation-driven/标签

    该标签会自动注册JSON数据解析的适配器HttpMessageConverter

  3. 前台页面发送JSON字符串给后台控制层

    前台页面需要发送JSON字符串给控制器的业务方法时,只需在业务方法形参上标注@RequestBody注解,就可以自动完成JSON字符串到POJO对象或集合的转换

    (1)ajax()函数中的data参数要写为JSON字符串的形式
    ​
    (2)ajax()函数中要指定contentType:"application/json"
    ​
     (3)ajax()函数中的type参数要写为POST,不能发送GET请求
    ​
     (4)业务方法形参上标注@RequestBody注解,表示该参数从POST请求的请求体中获取
    

    image-20210130031208888

  4. 后台控制器发送JSON字符串给前台页面 常用

    (1)控制器的业务方法需要返回JSON字符串给前台页面时,只需在业务方法上标注 @ResponseBody注解,就可以自动完成POJO对象或集合到JSON字符串的转换

    image-20210130031146699

    (2)前后端分离开发模式中,控制器只需返回JSON数据,可以在类上使用组合注解@RestController @RestController=@Controller+@ResponseBody

    image-20210130031230258

7. SpringMVC-拦截器

🌳 拦截器的基本概念

( 1 )拦截器(Interceptor)类似于Java Web的过滤器(Filter),可以在业务方法执行前后,添加预处理和后处理操作,也是AOP思想的实现

( 2 ) 拦截器拦截的是控制器的业务方法,不能像过滤器那样拦截JSP页面和静态资源

( 3 ) 前端核心控制器ServletDispatcher在执行某个控制器的业务方法之前,会根据配置的顺序依次执行所有自定义的拦截器中的方法,最终才到达控制器的业务方法中

🌳 拦截器的开发步骤

( 1 ) 创建拦截器,实现HandlerInterceptor接口

根据具体的业务需求,重写preHandle()方法,postHandle()方法,afterHandle()方法

(1.1)boolean preHandle()方法---业务方法执行之前执行的方法

返回true表示继续执行后续流程,返回false表示流程结束

    (1.2)void postHandle()方法---业务方法执行后前执行的方法

(1.3)void afterCompletion()方法---响应页面渲染后执行的方法

( 2 ) 在springmvc.xml中配置拦截器

(2.1)使用标签指定拦截器类的完整类名

(2.2)使用mvc:mapping标签指定拦截的路径范围,**表示所有请求

如/user/**表示拦截user下的全部请求

<mvc:interceptors><mvc:interceptor>    
        <mvc:mapping path="拦截的路径范围"/>
        <mvc:exclude-mapping path="不拦截的路径范围"/>
            <bean class="拦截器的完整类名"></bean>
    </mvc:interceptor>
 
</mvc:interceptors>

🌳 多个拦截器的执行顺序 (1)preHandle()方法的执行顺序和配置文件的定义顺序相同,postHandle()方法和afterCompletion()方 法正好相反

(2)preHandle()方法返回true,则afterCompletion()方法肯定会执行

8. SpringMVC-执行过程和源码分析

一. Spring MVC的四大核心组件

1. 前端控制器---DispatcherServlet

接收客户端的请求,并将请求配发给其它组件完成操作,最终将结果响应给客户端

2. 处理器映射器---HandlerMapping

根据请求的url查找匹配的Handler/Controller

业务处理器Handler/控制器Controller---需由程序员编写的,处理业务逻辑的控制器类

3. 处理器适配器---HandlerAdapter

根据特定的规则,真正执行Handler/Controller,以及配置的拦截器

4. 视图解析器---ViewResolver

根据逻辑视图名生成真正的View视图对象,支持多种View视图技术

二. SpringMVC的执行流程 重点 【企业面试题】

image-20210130032156791

1. 客户端发送请求,前端控制器(DispatcherServlet)接收客户的请求,并将请求派发给处理器映射器(HandlerMapper)

【DispatcherServlet相当于Struts 2的StrutsPrepareAndExecuteFilter】

【HandlerMapper相当于Struts 2的ActionMapper】

2. HandlerMapper根据请求的url和匹配规则,找到具体的业务处理器(Handler/Controller),并根据SpringMVC的配置文件中相关的拦截器配置,获取到一系列的拦截器(Interceptor),并将Handler和这些Inteceptor串联成一个HanderExecutionChain,返回给DispatcherServlet

【Handler/Controller---业务处理器,也叫后端控制器,由程序员编写,相当于Struts的Action】

3. DispatcherServlet委托处理器适配器(HandlerAdapter)按照特定规则执行处理器中的业务方法

【HandlerAdapter相当于Struts 2的ActionInvocation】

4. 业务方法返回一个ModelAndView对象给HandlerAdapter,该对象内部封装了视图所需的模型数据和视图的逻辑名,HandlerAdapter将这个ModelAndView对象继续返回给DispatcherServlet

5. DispatcherServlet将ModelAndView对象派发给视图解析器(ViewResolver)去解析ModelAndView对象中的内容,返回View视图对象给DispatcherServlet

【ModelAndView相当于Struts 2的result + 放入属性范围中的数据】

6. DispatcherServlet从ModelAndView对象中取出数据部分(Model),并将数据填充到视图中,此过程叫视图渲染

7. DispatcherServlet根据视图类型,进行相应的请求转发或重定向操作,最终将结果响应给客户端

image-20210130032214050