SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc

源码(码云):https://gitee.com/yin_zhipeng/implement-_springmvc_of_myself.git

文章目录

  • 一、手写SpringMVC
  • 二、SpringMVC高级应用
    • 1. 环境搭建(很重要,必须知道为什么这么用)
    • 2. SpringMVC请求处理流程和九大组件
    • 3. url-pattern配置和原理
    • 4. 数据输出Model、Map、ModelMap,请求参数绑定
    • 5. Filter处理Post请求乱码,以及请求方式转换
    • 6. 监听器、Spring MVC拦截器、过滤器
      • 6.1 单个拦截器执行流程
      • 6.2 多个拦截器执行流程
    • 7. 处理文件上传Multipart形式数据
    • 8. 异常处理机制
    • 9. springMVC重定向参数传递flash属性
  • 三、SpringMVC源码

一、手写SpringMVC

篇幅原因,我将其放在这篇文章https://blog.csdn.net/grd_java/article/details/123000127

二、SpringMVC高级应用

Spring MVC
  1. 对servlet进行封装,简化servlet开发的框架
  2. 可以接收请求,返回响应,跳转页面,Servlet能干的它都可以
  3. 是Spring Framework的衍生功能,由于功能完善,我们一般当它为单独的一个框架
  4. 是现在最主流的MVC框架之一,Spring 3.0发布后,全面超越Struts2成为最优秀MVC框架
与原生Servlet的区别
  1. 原生Servlet,当前端发送请求时,根据url找到对应Servlet。有多少的业务,写多少Servlet
  2. SpringMVC,全局只有一个Servlet叫DispatchServlet,它仅仅通过我们请求的url和参数,调用相应的方法。我们不用编写任何Servlet了,只需要编写相关业务方法,再方法上加上注解(@RequestMapping、@GetController等)
    SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc_第1张图片
  3. SpringMVC的优势,我们不用写Servlet,全局只需要维护一个DispatchServlet,任何国际化配置,扩展配置,都只针对一个Servlet
    SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc_第2张图片

1. 环境搭建(很重要,必须知道为什么这么用)

  1. 创建Maven的web工程,引入相关依赖
    SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc_第3张图片
  <packaging>war</packaging>

  <name>advanced_application_of_springmvc Maven Webapp</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>8</maven.compiler.source>
    <maven.compiler.target>8</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
    <!--spring MVC-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.1.12.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
      <scope>provided</scope><!--tomcat如果有,不使用这个使用自己的,避免冲突-->
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <!--指定编译级别-->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId><!--加个插件-->
        <version>3.1</version>
        <configuration>
          <source>8</source><!--JDK-->
          <target>8</target><!--编译-->
          <encoding>utf-8</encoding><!--编码-->
<!--          <compilerArgs>-->
<!--            <arg>-parameters</arg>&lt;!&ndash;告诉编译器,编译时,记录形参的真实名称&ndash;&gt;-->
<!--          </compilerArgs>-->
        </configuration>
      </plugin>
      <!--tomcat 7插件-->
      <plugin>
        <groupId>org.apache.tomcat.maven</groupId>
        <artifactId>tomcat7-maven-plugin</artifactId>
        <version>2.2</version>
        <configuration>
          <port>8080</port>
          <path>/</path>
          <uriEncoding>UTF-8</uriEncoding>
        </configuration>
      </plugin>
    </plugins>
  </build>
  1. 我们说过,SpringMVC只有一个Servlet,DispatchServlet。所以我们需要在web.xml中配置这个Servlet的映射。另外,我们还需要配置这个Servlet拦截哪些请求(它源码里面会解析,然后拦截相应的请求)
    SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc_第4张图片
  2. 我们写一个jsp页面进行跳转用
    SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc_第5张图片
  3. 编写controller,我们只需要通过springMVC注解标识即可,前面手写框架我们已经很清楚了,他就是根据我们配置的扫描路径,扫描相应加了注解的组件,添加到springIOC容器,并且建立url和方法的映射(initHandlerMapping和Handler)
  1. 我们发现下面跳转视图时,目录结构写死了,后缀名也写死了,这是非常耦合的。我们最好将这些统一配置在一个地方,这样一旦变化,只需要修改一个地方
  2. 而springmvc提供了配置视图解析器的功能
  3. 同时我们也得配置springMVC扫描路径,我们将类写在这里,springMVC不知道这是需要扫描注解的类
    SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc_第6张图片
  4. 我们希望可以写成这样的
    SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc_第7张图片
  1. 编写spring配置文件,配置springmvc,我们需要知道springmvc扫描路径,因为我们通过注解标识Controller类,我们得告诉springmvc去哪找这些类。还得配置视图解析器,解耦合
  1. 开启注解扫描,就是spring的东西,springmvc基于spring,没什么好说的
  2. 配置视图解析器,InternalResourceViewResolver,这是一个实体类,只要配置上,就可以生效,因为配置好后,就会被加载到ioc容器,那么springMVC解析时,查一下ioc容器,如果有,就进行视图解析
  3. InternalResourceViewResolver这个类有两个重要属性,prefix和suffix,表示解析的前缀和后缀,我们通过setter方法为其赋值即可,spring使用setter赋值形式就是< property name=“prefix” value="/WEB-INF/jsp/" />
    SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc_第8张图片
  1. 选择性配置,处理器映射器和处理器适配器。这两个都有默认实现,如果不配置就用默认的,我们可以自己配置,选择最适合我们的。我们可以添加一个注解驱动
    SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc_第9张图片
<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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd
">

    
    <context:component-scan base-package="com.yzpnb.controller">context:component-scan>
    
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/">property>
        <property name="suffix" value=".jsp">property>
    bean>
    
    <mvc:annotation-driven>mvc:annotation-driven>
beans>
  1. web.xml加载配置文件。经过首先springMVC我们知道,DispatcherServlet中,会读取servlet配置,获取我们springmvc配置文件的位置,然后根据配置文件,进行springmvc的初始化,所以我们需要在web.xml中指定配置文件位置
    SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc_第10张图片
DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Applicationdisplay-name>
  
  <servlet>
    <servlet-name>springmvcservlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
    <init-param>
      <param-name>contextConfigLocationparam-name>
      <param-value>classpath:springmvc.xmlparam-value>
    init-param>
  servlet>
  <servlet-mapping>
    <servlet-name>springmvcservlet-name>
    
    <url-pattern>/url-pattern>
  servlet-mapping>
web-app>
启动服务器,测试

SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc_第11张图片

2. SpringMVC请求处理流程和九大组件

请求处理流程

SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc_第12张图片

  1. 用户发送请求至前端控制器DispatcherServlet
  2. DispatcherServlet收到请求调用HandlerMapping处理器映射器
  3. 处理器映射器根据请求URL找到具体的Handler(后端控制器),生成处理器对象及处理器拦截器(如果有则生成)一并返回DispatcherServlet
  4. DispatcherServlet调用HandlerAdapter处理器适配器去调用Handler
  5. 处理器适配器执行Handler
  6. Handler执行完给处理器适配器返回ModelAndView
  7. 处理器适配器向前端控制器返回ModelAndView,ModelAndView是Spring MVC框架的一个底层对象,包括Model和View
  8. 前端控制器请求视图解析器去进行视图解析,根据逻辑视图名来解析真正的视图。
  9. 视图解析器向前端控制器返回View
  10. 前端控制器进行视图渲染,就是将模型数据(在ModelAndView对象中)填充到request域
  11. 前端控制器向用户响应结果
9大组件(需要掌握/了解即可)
  1. HandlerMapping处理器映射器
  1. 处理Handler(处理器和url的映射)。用来查找Handler,Handler是处理器,可以是类也可以是方法。例如标记了@RequestMapping的每个方法都可以看做是Handler。Handler负责实际请求处理业务。HandlerMapping的作用便是找到与请求相应的处理器Handler和Interceptor
  1. HandlerAdapter处理器适配器
  1. 装饰设计模式,根据不同的类型,进行Handler的相应适配,SpringMVC中Handler可以是任意形式的,只要能处理请求即可
  2. 把请求交个Servlet时,由于Servlet方法结构都是doService(HttpServletRequest req,HttpServletResponse resp)形式,要让固定Servlet处理方法调用Handler来进行处理,便是HandlerAdapter的职责
  1. HandlerExceptionResolver处理器异常解析器
  1. 用于处理Handler产生的异常情况,作用是根据异常设置ModelAndView,之后交给渲染方法进行渲染,渲染方法会将ModelAndView渲染成页面
  1. ViewResolver视图解析器
  1. 视图解析器,用于将String类型的视图名和Locale解析为View类型视图,只有一个resolveViewName()方法。
  2. Controller层返回的String类型视图名viewName最终会在这里被解析为View(用来渲染页面的,会将程序返回的参数和数据填入模板中,生成html文件)
  3. 完成工作:找到渲染所用模板,找到所用技术(视图类型,例如JSP)填入参数
  4. Spring MVC自动装配InternalResourceViewResolver,针对JSP类型视图的
  1. RequestToViewNameTranslator请求到视图名翻译(转换)器
  1. 从请求中获取ViewName,我们知道ViewResolver根据ViewName查找View,但是有的Handler处理完成之后,没有设置View,也没有设置ViewName,那么此组件可以从请求中查找ViewName
  2. 就是我们不指定modelAndView.setViewName(“success”);此时视图解析器拼接出来的地址是/WEB-INF/jsp/请求名.jsp,也就是把请求解释成了ViewName。
  1. LocalResolver地址解析器
  1. 主要处理国际化的一些东西。核心方法resolveViewName方法需要两个参数,视图名和Locale
  2. 用于从请求中解析出Locale,例如中国Locale是zh-CN,用于表示一个区域,此组件是i18n的基础
  1. ThemeResolver主题处理器
  1. 解析主题(样式、图片和它们形成的显示效果的集合)。SpringMVC中一套主题对应一个properties文件,里面存放与当前主题相关的所有资源。
  2. 创建主题只需要准备好资源,建立一个“主题名.properties”文件将资源设置进去,放在classpath下,就可以在页面中使用
  3. SpringMVC与主题相关的类有ThemeResolver(负责从请求解析主题名)、ThemeSource(负责根据主题名找具体主题,具体抽象是Theme)和Theme(可以通过Theme获取主题和具体资源)
  1. MultipartResolver复合解析器
  1. 封装普通请求,使其具有文件上传功能,通过将普通请求包装成MultipartHttpServletRequest来实现
  2. 可以通过getFile()方法直接获取文件。如果上传多个,可以调用getFileMap()方法得到Map结构
  1. FlashMapManager:FlashMap管理者
  1. 管理FlashMap,FlashMap用于重定向时传递参数,例如用户处理订单,为了避免重复提交,处理完Post请求后重定向到get请求来显示订单详情之类信息。
  2. 重定向时,虽然可以把参数写进URL(很low,不推荐)。可以通过FlashMap来传递,只需要重定向时将要传递数据写入请求的OUTPUT_FLASH_MAP_ATTRIBUTE属性中(请求可以通过ServletRequestAttributes.getRequest()获取)
  3. 这样重定向之后,Handler中Spring会自动将其设置到Model中,显示订单信息页面上可以直接从Model中获取数据

3. url-pattern配置和原理

前面我们web.xml配置了DispatcherServlet拦截路径为< url-pattern>/< /url-pattern>,可以拦截所有url请求。接下来详细介绍这个配置,以及剖析其原理
  1. < url-pattern>/< /url-pattern>虽然不拦截.jsp这种,但是会拦截所有静态资源(css,js,html),原因如下
  1. 因为tomcat有一个web.xml可以理解为父xml,而我们编写的是子xml,属于继承关系
  2. 我们可以看到tomcat的web.xml有一个DefaultServlet映射,根据注释得知,它是提供静态资源服务的
    SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc_第13张图片
  3. 而它的url-pattern就是配置的一个/。jsp反而是单独处理的
    SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc_第14张图片
  4. 而我们编写的子web.xml中也配置了"/",所以子类覆盖父类,当url请求过来时,不会走DefaultServlet处理静态资源了,而是直接到我们配置的web.xml,走DispatcherServlet了
  5. 这也是为什么静态资源无法访问,但是jsp可以访问的原因
那么既然配置"/"会拦截静态资源,我们应该如何解决问题呢?方案一:配置文件中配置< mvc:default-servlet-handler/>,表示遇到静态资源请求url,就交给defaultServlet处理(Tomcat)
  1. springmvc中配置静态资源,解决问题< mvc:default-servlet-handler/>。此注解可以在SpringMVC上下文当中定义一个DefaultServletHttpRequestHandler对象,它会检查进入DispatcherServlet的url,如果是静态资源,就交给web服务器tomcat的DefaultServlet处理,如果不是,就springMVC继续处理
    SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc_第15张图片
  2. 测试
  1. 提供html
    SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc_第16张图片
  2. 测试
    SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc_第17张图片
上面的方法,有很大局限,静态文件只能放在webapp文件夹中(建立目录也可以),不能放入WEB-INF目录下,因此还有方案二:SpringMVC自己处理静态资源< mvc:resources mapping="/resources/**" location=“classpath:/”/>

SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc_第18张图片


    
    <mvc:default-servlet-handler>mvc:default-servlet-handler>
    
    <mvc:resources mapping="/static/**,/resources/**" location="classpath:/"/>

4. 数据输出Model、Map、ModelMap,请求参数绑定

一般我们使用springboot时,很少直接使用ModelAndView来进行Model数据处理,而他也是使用BindingAwareModelMap处理数据,反射通过BindingAwareModelMap做了很多事
  1. 我们看ModeAndView的源码中定义,对应数据,其实就是ModelMap
    SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc_第19张图片
  2. 也就是说,可以直接使用ModelMap,但是的借助SpringMVC的机制
  1. 我们可以直接把ModelMap当做参数进行传递,经过手写SpringMVC我们知道,DispatcherServlet反射方法时,会从Handler中获取方法参数,当发现参数是request、response、modelMap这些特殊对象时,是会帮我们传过来的
  2. 另外,我们返回值时,也会进行相应处理,比如我们直接返回字符串,就可以完成视图跳转渲染
    SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc_第20张图片
  1. 另外还可以使用Model
    SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc_第21张图片
  2. 甚至使用Map
    SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc_第22张图片
而它们几个其实都是BindingAwareModelMap实例,只要是给BindingAwareModelMap保存的内容,都会保存到请求域中
  1. 首先当SpringMVC反射方法时,发现Model之类的参数,会直接传一个BindingAwareModelMap,具体可以参考类图
    SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc_第23张图片
接下来看请求参数的绑定,我们先前手写SpringMVC框架时,绑定请求参数时,通过位置和参数名,绑定到相应方法参数(Handler中的参数映射容器中),对于Servlet API(request、response)直接进行传递(DispatcherServlet的req和resp)
  1. 默认支持Servlet API作为方法参数,反射方法时,如果需要直接将request和response和httpSession等待Servlet API传入
  2. 简单类型参数的支持,推荐使用包装数据类型,因为基础数据类型不能为null,很麻烦
  1. Integer、String、Float、Double、Boolean都支持。并且我们不推荐int、float、double、boolean
  2. 布尔类型的参数值为true或false,也可以使用1或0
  3. 使用时,直接声明形参即可,但是参数名和前端传递参数名需要一致。如果不一致,可以使用@RequestParam注解进行手动映射
  1. 支持实体类参数(Pojo,java类)
  2. 支持SpringMVC封装的一些类,比如接收用户上传文件的,比如ModelMap
  3. 原理就是DispatcherServlet接收到请求后,从request中获取传输的参数(例如request.getParameter(“xxxx”)),获取获取json数据。然后根据Handler中保存的参数信息,进行合理封装
那么SpringMVC不支持的参数绑定,就会无法自动转换,比如前端给一个日期类型字符串,我们无法将其解析成日期类型。这时候就需要SpringMVC类型转换器,我们需要继承接口并实现,然后配置文件中注册它
  1. 可见字符串无法直接转换为日期,我们要做的就是当遇到这种情况,让SpringMVC自动调用我们的转换器来进行转换
    在这里插入图片描述
  1. 字符串对日期类型转换器,需要实现Converter<源类型, 目标类型>接口
    SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc_第24张图片
import org.springframework.core.convert.converter.Converter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * Converter<源类型, 目标类型>
 */
public class DateConverter implements Converter<String, Date> {
    /**
     * 将源类型转换为目标类型的逻辑
     */
    @Override
    public Date convert(String source) {
        //字符串转日期
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        try {
            Date parse = simpleDateFormat.parse(source);
            return parse;
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return null;
    }
}
  1. 通过FormattingConversionServiceFactoryBean工厂,注册它,可以通过@Bean注解,也可以xml配置文件中配置,我们就用xml配置
  1. 首先这是工厂Bean,它里面有set集合,保存所有转换器
    SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc_第25张图片
  2. xml把我们的转换器配置进去
    SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc_第26张图片
    
    <bean id="conversionServiceBean" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <bean class="com.yzpnb.converter.DateConverter">bean>
            set>
        property>
    bean>
  1. 光配置进去还不行,我们还得指定处理器适配器使用,因为处理器适配器是负责Handler绑定的,我们需要进行关联
    SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc_第27张图片
    <mvc:annotation-driven conversion-service="conversionServiceBean">mvc:annotation-driven>
  1. 测试
    SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc_第28张图片

5. Filter处理Post请求乱码,以及请求方式转换

太基础的对于怎么搞Post就不说了,这里主要讲如何用Filter过滤器处理Post乱码
  1. 和Servlet一样,我们可以在web.xml中配置过滤器,也可以通过注解,这里就用xml配置。但是我们使用SpringMVC封装好的过滤器rg.springframework.web.filter.CharacterEncodingFilter
    SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc_第29张图片
get请求乱码需要修改tomcat下server.xml文件的配置
<Connector URIEncoding="utf-8" connectionTimeout="2000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>
一般我们通过指定请求_method参数来表示当前请求类型,比如put,delete。这也需要过滤器来解析

在这里插入图片描述

  
  <filter>
    <filter-name>hiddenHttpMethodFilterfilter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilterfilter-class>
  filter>
  <filter-mapping>
    <filter-name>hiddenHttpMethodFilterfilter-name>
    <url-pattern>/*url-pattern>
  filter-mapping>

6. 监听器、Spring MVC拦截器、过滤器

三者区别
  1. Servlet处理Request请求和Response响应
  2. 过滤器Filter:对Request请求起过滤作用,在Servlet前面过滤
  3. 监听器Listener:实现类javax.servlet.ServletContextListener接口的服务端组件,随Web应用启动而启动,只初始化一次,然后一直监听,Web应用停止就会销毁
  1. 通常做一些初始化工作,web应用中spring容器启动ContextLoaderListener监听器
  2. 也会用来监听web中特定事件,例如HttpSession,ServletRequest的创建和销毁,变量创建、销毁、修改等,可以在某些动作前后增加处理,实现监控,例如统计在线人数,利用HttpSessionLisener等
  1. 拦截器Interceptor:SpringMVC、Struts等表现层框架特有,不拦截jsp/html/css/image等资源访问,只会拦截访问的控制器方法(Handler)
  1. Handler业务执行前拦截一次
  2. Handler业务执行完但未跳转页面前拦截一次
  3. 跳转页面之后拦截一次
    SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc_第30张图片
  1. Servlet、filter、listener配置在web.xml,interceptor配置在表现层框架配置文件中

6.1 单个拦截器执行流程

执行流程如下,建议先看下面知道如何实现单拦截器,然后回来看流程

SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc_第31张图片

  1. 先是自定义拦截器的preHandle方法,请求刚来的springMVC就被拦截
  2. 当preHandle为true后,向下HandlerAdapter进行匹配,匹配合适的处理器,进行逻辑处理,也就是Handler方法的执行
  3. 然后就是自定义拦截器的postHandle,结果返回给DispatcherServlet之前进行拦截
  4. 然后交给DispatcherServlet进行请求结果响应
  5. 响应完成后,执行自定义拦截器的afterCompletion
单个拦截器
  1. 继承HandlerInterceptor接口,实现相关方法即可
    SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc_第32张图片
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyInterceptorOne implements HandlerInterceptor {
    /**
     * handler执行前拦截,常用于权限校验
     * @return true表示放行,false表示不放行
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("MyInterceptorOne====>>>>preHandle");
        return true;
    }
    /**下面两个方法都不常用**/
    //handler执行完,还没跳转页面前拦截
    //modelAndView可以让我们,还没跳转时,对数据进行修改,一般不会有这种需求
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("MyInterceptorOne====>>>>postHandle");
    }
    //handler执行完,跳转页面后拦截
    //ex 可以让我们进行异常捕获,但我们有异常处理器,压根用不到
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("MyInterceptorOne====>>>>afterCompletion");
    }
}
  1. springmvc配置文件配置拦截器
    SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc_第33张图片
    
    <mvc:interceptors>
        

        
        <mvc:interceptor>
            <mvc:mapping path="/dome/**"/>
            

            
            <bean class="com.yzpnb.interceptor.MyInterceptorOne">bean>
        mvc:interceptor>
    mvc:interceptors>
  1. 测试
    SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc_第34张图片

6.2 多个拦截器执行流程

如果有多个拦截器,如何执行呢,代码都一样,就不赘述了

SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc_第35张图片

  1. 只要这样理解就可以轻松记住,拦截器执行就像一个栈数据结构,先进后出,后进先出
  2. 比如第一次preHandle拦截,先是拦截器1然后拦截器2。而第二次postHandle拦截,先是拦截器2,然后才是拦截器1,第三次afterCompletion,先是拦截器2,然后才是拦截器1
  3. 把第一次拦截当成入栈,剩余的两次拦截,当成出栈,就可以了。

7. 处理文件上传Multipart形式数据

传统SSM项目需要jar包依赖,spirngBoot不需要

SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc_第36张图片

    <!--文件上传-->
    <dependency>
      <groupId>commons-fileupload</groupId>
      <artifactId>commons-fileupload</artifactId>
      <version>1.3.1</version>
    </dependency>
第二步需要配置文件上传解析器的Bean实例,id就是固定的multipartResolver,不要随意起名

SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc_第37张图片

    
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        
        <property name="maxUploadSize" value="1000000000">property>
    bean>
第三步,参数列表中指定MultipartFile即可,和ModelMap一样,springMVC会在反射时进行参数的封装,而像Request,MultipartFile这样的都会特殊处理。注意,参数名,需要和前端对应,否则需要注解指定和前端参数名对应,例如@RequestParam(“fileUpload”) MultipartFile file

SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc_第38张图片

8. 异常处理机制

(不推荐的方法)我们可以在controller中通过注解@ExceptionHandler(异常类型.class)来捕获相应异常

SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc_第39张图片

使用注解@ControllerAdvice声明一个类

SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc_第40张图片

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@ControllerAdvice
public class ExceptionAdvice {
    /**
     * @ExceptionHandler(要捕获的异常类型.class)
     * @param exception 捕获到的异常,这里一定要比ExceptionHandler注解指定的类型大(或等于)。否则无法强制转换
     */
    @ExceptionHandler(ArithmeticException.class)
    public void handleException(ArithmeticException exception, HttpServletResponse response){
        try{
            response.getWriter().write("ExceptionAdvice====>>>>handleException"+exception.getMessage());
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

9. springMVC重定向参数传递flash属性

虽然用的不多,但是也需要了解。重定向url会改变,参数会丢失,两个请求。转发,url不会变,参数不丢失,一个请求
  1. 需求,A重定向到B方法,参数不丢失
    SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc_第41张图片
    @RequestMapping("/A")
    public String A(String name, RedirectAttributes redirectAttributes){
        //方式一,直接字符串拼接
//        return "redirect:B?name="+name;

        //方式二,flash属性,暂存到session中,跳转页面之后,属性销毁
        redirectAttributes.addFlashAttribute("name",name);
        return "redirect:B";

    }
    @RequestMapping("/B")
    public void B(@ModelAttribute("name") String name){
        System.out.println(name);
    }

三、SpringMVC源码

篇幅原因,我将其放在这篇文章https://blog.csdn.net/grd_java/article/details/123000880

你可能感兴趣的:(源码,git,java,springmvc,框架)