SpringMVC框架学习笔记(八):自定义拦截器和异常处理

1 自定义拦截器

1.1 什么是拦截器

1.1.1 说明

(1)Spring MVC 也可以使用拦截器对请求进行拦截处理,用户可以自定义拦截器来实现特定 的功能.

(2)自定义的拦截器必须实现 HandlerInterceptor 接口

1.1.2****自定义拦截器的三个方法

(1)preHandle():这个方法在业务处理器处理请求之前被调用,在该方法中对用户请求 request 进行处理。

(2)postHandle():这个方法在目标方法处理完请求后执行

(3)afterCompletion():这个方法在完全处理完请求后被调用,可以在该方法中进行一些资源 清理的操作。

1.2 自定义拦截器执行流程分析图

自定义拦截器执行流程说明

(1)如果 preHandle 方法返回 false, 则不再执行目标方法, 可以在此指定返回页面

(2)postHandle 在目标方法被执行后执行. 可以在方法中访问到目标方法返回的 ModelAndView 对象

(3)若 preHandle 返回 true, 则 afterCompletion 方法 在渲染视图之后被执行

(4)若 preHandle 返回 false, 则 afterCompletion 方法不会被调用

(5)在配置拦截器时,可以指定该拦截器对哪些请求生效,哪些请求不生效

1.3 自定义拦截器应用实例

1.3.1 快速入门

**应用实例需求:**完成一个自定义拦截器,学习如何配置拦截器和拦截器的运行流程

代码实现:

(1)创建 MyInterceptor01.java 类,实现 HandlerInterceptor 接口

package com.web.interceptor;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

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

@Component
public class MyInterceptor01 implements HandlerInterceptor {

    /**
     * 1.preHandle()在目标方法被执行前执行
     * 2.如果preHandle()返回false,就不会执行目标方法
     * 3.该方法可以获取到request ,response,handler
     * 4.这里根据业务,可以进行拦截,并指定跳转到哪个页面
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("--MyInterceptor01--preHandle()--");
        return true;
    }

    /**
     * 1. 在目标方法执行后,会执行postHandle()
     * 2.
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("--MyInterceptor01--postHandle()--");
    }

    /**
     * 1. 该方法在视图渲染后被执行,这里可以进行资源清理
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("--MyInterceptor01--afterCompletion()--");
    }
}

(2)修改 spring 容器配置文件,增加以下配置

<!--配置自定义拦截器-->
<mvc:interceptors>
    <!--
    1 第一种配置方式,直接使用ref 引用到对应的myInterceptor01
    2 这种方式,会拦截所有的目标方法
    -->
    <ref bean="myInterceptor01"/>

    <!--
    1.第二种配置方式,mvc:mapping path="/hi" 指定要拦截的路径
    2.ref bean="myInterceptor01" 指定对哪个拦截器进行配置
    -->
    <mvc:interceptor>
        <mvc:mapping path="/hi"/>
        <ref bean="myInterceptor01"/>
    </mvc:interceptor>

    <!--
    1. 第3种配置方式
    2. mvc:mapping path="/h*" 通配符方式 表示拦截 /h 打头的路径
    3. mvc:exclude-mapping path="/hello" 表示/hello不拦截
    4. ref bean="myInterceptor01" 指定对哪个拦截器配置
    -->
    <mvc:interceptor>
        <mvc:mapping path="/h*"/>
        <mvc:exclude-mapping path="/hello"/>
        <ref bean="myInterceptor01"/>
    </mvc:interceptor>
</mvc:interceptors>

(3)创建 InteHandler.java 类

package com.web.interceptor;

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

@SuppressWarnings("All")
@Controller
public class InteHandler {

    @RequestMapping("/hi")
    public String hi(){
        System.out.println("--InteHandler--hi()方法被执行");
        return "success";
    }

    @RequestMapping("/hello")
    public String hello(){
        System.out.println("--InteHandler--hello()方法被执行");
        return "success";
    }
}

(4)创建前端页面 interceptor.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>测试自定义拦截器</title>
</head>
<body>
<h1>测试自定义拦截器</h1>
<a href="<%=request.getContextPath()%>/hi">测试自定义拦截器-hi</a><br><br>
<a href="<%=request.getContextPath()%>/hello">测试自定义拦截器-hello</a><br/><br/>
</body>
</html>

(5)测试效果

1.3.2 注意事项和细节

(1)拦截器需要配置才生效,不配置是不生效的.

(2)如果 preHandler() 方法返回了 false, 就不会执行目标方法(前提是你的目标方法被拦截 了), 可以在这里根据业务需要指定跳转页面.

1.4 多个拦截器

1.4.1 多个拦截器执行流程示意图

1.4.2 应用实例****1

(1)创建 MyInterceptor02.java ,作为第二个拦截器

package com.web.interceptor;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

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

@Component
public class MyInterceptor02 implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("--MyInterceptor02--preHandle()--");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("--MyInterceptor02--postHandle()--");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("--MyInterceptor02--afterCompletion()--");
    }
}

(2)在spring容器配置文件 xml文件 中给/hi 配置这两个拦截器

**注意:**拦截器的执行顺序是和配置顺序一致的

<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/hi"/>
        <ref bean="myInterceptor01"/>
    </mvc:interceptor>

    <mvc:interceptor>
        <mvc:mapping path="/hi"/>
        <ref bean="myInterceptor02"/>
    </mvc:interceptor>
</mvc:interceptors>

(3)依然使用前面快速入门案例中的 interceptor.jsp 页面进行测试

1.4.3 注意事项和细节

  • 如果第 1 个拦截器的 preHandle() 返回 false , 后面都不在执行
  • 如 果 第 2 个 拦 截 器 的 preHandle() 返 回 false , 就 直 接 执 行 第 1 个 拦 截 器 的 afterCompletion()方法, 如果拦截器更多,规则类似
  • 说明: 前面说的规则,都是目标方法被拦截的前提

1.4.2****应用实例 2

需求: 如果用户提交的数据有禁用词(比如 病毒),则在第 1 个拦截器就返回,不执行目标方法

(1)修改 MyInterceptor01 类中的 preHandle() 方法

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    System.out.println("--MyInterceptor01--preHandle()--");
    if ("病毒".equals(request.getParameter("topic"))){
        request.getRequestDispatcher("/WEB-INF/pages/warning.jsp")
                .forward(request, response);
        return false;
    }
    return true;
}

(2)创建警告页面 warning.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>警告</title>
</head>
<body>
<h1>不要乱说话哦</h1>
</body>
</html>

(3)使用 postman 测试

2 异常处理

2.1 异常处理**-**基本介绍

(1)Spring MVC 通过 HandlerExceptionResolver 接口处理程序的异常,包括 Handler 映射、数据绑定以及目标方法执行时发生的异常。
(2)使用 Handler 中用 @ExceptionHandler 注解定义的方法 来处理目标方法发生的异常,这就是局部异常。
(3)ExceptionHandlerMethodResolver 类内部若在 Handler 本类中找不到@ExceptionHandler 注解的话,会找 @ControllerAdvice 类的@ExceptionHandler 注解方法, 这样就相当于一个全局异常处理器

(4)异常处理时:局部异常 优先级高于 全局异常,如果异常在局部处理了,就不会进行全局处理

2.2 局部异常

说明:使用@ExceptionHandler注解标注局部异常,该注解的value值可指定要处理的异常类型

实际案例:演示局部异常处理机制

(1)创建 HandlerException.java 类

package com.web.exception;

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

import javax.servlet.http.HttpServletRequest;

@Controller
public class HandlerException {

    /**
     * 1. localException 方法处理局部异常
     * 2. 这里处理ArithmeticException.class算术异常,NullPointerException.class空指针异常
     * 3. Exception exception: 生成的异常对象,会传递给exception, 通过exception可以得到相关的信息
     */
    @ExceptionHandler({ArithmeticException.class, NullPointerException.class})
    public String localException(Exception exception, HttpServletRequest request){
        System.out.println("错误信息--"+exception.getMessage());
        request.setAttribute("result", exception.getMessage());
        return "exception_mes";
    }

    /**
     * 1. 编写方法,模拟算术异常
     * 2. 如果我们不做异常处理,是由tomcat默认页面显示
     */
    @RequestMapping("/testException01")
    public String test01(Integer num){
        int i = 9/num;
        return "success";
    }

}

(2)创建前端测试页面 test_exception.jsp 和 异常页面 exception_mes.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>测试异常</title>
</head>
<body>
<h1>测试异常</h1>
<a href="testException01?num=0">这是个算术异常</a>
</body>
</html>

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>出现异常</title>
</head>
<body>
<h1>出现了异常</h1>
异常信息为--${requestScope.result}
</body>
</html>

(3)测试效果如下

2.3 全局异常

**说明:**使用 @ControllerAdvice 标注一个类, 这个类就相当于一个全局异常处理器

应用实例:

**需求说明:**演示全局异常处理机制 , ExceptionHandlerMethodResolver 内部若在 Handler 本类中找不到 @ExceptionHandler 注解的话,会找 @ControllerAdvice 类的@ExceptionHandler 注解方法, 这样就相当于一个全局异常处理器

(1)创建 GlobalException.java 作为一个全局处理器

package com.web.exception;

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

import javax.servlet.http.HttpServletRequest;

@ControllerAdvice
public class GlobalException {

    /**
     * 1. 全局异常就不管是哪个Handler抛出的异常,都可以捕获 , @ExceptionHandler({异常类型})
     * 2. 这里处理的全局异常是NumberFormatException.class,ClassCastException.class
     * 3. Exception exception 接收抛出的异常对象
     */
    @ExceptionHandler({NumberFormatException.class, ClassCastException.class})
    public String globalException(Exception exception, HttpServletRequest request){
        System.out.println("全局异常信息:" + exception.getMessage());
        request.setAttribute("result", exception);
        return "exception_mes";
    }
}

(2)在 HandlerException 类中增加方法 global()

@RequestMapping("/testGlobalException")
public String global(){
    //1. 这里我们模拟了一个异常 NumberFormatException
    //2. 该异常没有在局部异常处理,按照异常处理机制,就会交给全局异常处理类处理
    Integer i = Integer.parseInt("str");
    return "success";
}

(3)创建前端请求

<a href="testGlobalException">点击测试全局异常</a>

(4)启动服务器,测试效果

2.4 自定义异常

说明:通过@ResponseStatus 注解, 可以自定义异常,该注解有两个属性reason异常的原因,value异常的状态。自定义异常需要继承异常类型,比如 RuntimeException

应用实例

(1)创建自定义异常 AgeException.java

package com.web.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(reason = "年龄需要在1-120之间", value = HttpStatus.BAD_REQUEST)
public class AgeException extends RuntimeException{

    public AgeException(){}

    public AgeException(String message){
        super(message);
    }
}

(2)修改 ExceptionHandler.java, 增加方法

@RequestMapping("/testException02")
public String test02(){
    throw new AgeException("年龄必须在1-120之间~~~");
}

(3)修改 test_exception.jsp, 增加超链接

<a href="testException02">点击测试自定义异常</a>

(4)启动服务器,测试效果

(5)将该异常添加至全局异常处理器

@ExceptionHandler({NumberFormatException.class, ClassCastException.class, AgeException.class})
public String globalException(Exception exception, HttpServletRequest request){
    System.out.println("全局异常信息:" + exception.getMessage());
    request.setAttribute("result", exception);
    return "exception_mes";
}

(6)重新部署,测试效果

2.5 SimpleMappingExceptionResolver 异常统一处理

2.5.1 基本说明

  • 如果希望对所有异常进行统一处理,可以使用 SimpleMappingExceptionResolver
  • 它将异常类名映射为视图名,即发生异常时使用对应的视图报告异常
  • 需要在 ioc 容器中配置

2.5.2 应用实例

需求说明:对数组越界异常进行统一处理,使用 SimpleMappingExceptionResolver

(1)配置 spring 容器配置文件

<!--配置统一处理异常Bean-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <property name="exceptionMappings">
        <props>
            <prop key= "java.lang.ArrayIndexOutOfBoundsException">arrEx</prop>
        </props>
    </property>
</bean>

(2)修改 ExceptionHandler.java , 增加方法 test03

RequestMapping(value = "/testException03")
public String test03(){
    int[] arr = new int[]{3,9,10,190};
    //抛出一个数组越界的异常 ArrayIndexOutOfBoundsException
    System.out.println(arr[90]);
    return "success";
}

(3)创建/WEB-INF/pages/arrEx.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>数组越界异常</title>
</head>
<body>
<h1>异常信息: 数组越界异常</h1>
</body>
</html>

(4)修改 test_exception.jsp

<a href="<%=request.getContextPath()%>/testException03">点击测试统一处理异常</a>

(5)启动服务器,测试效果

2.5.3 对未知异常进行统一处理

说明:对未知异常进行统一处理,使用 SimpleMappingExceptionResolver

应用实例:

(1) 修改 spring 容器配置文件,添加异常类型 Exception

<!--配置统一处理异常Bean-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <property name="exceptionMappings">
        <props>
            <prop key= "java.lang.ArrayIndexOutOfBoundsException">arrEx</prop>
            <prop key="java.lang.Exception">allEx</prop>
        </props>
    </property>
</bean>

(2)修改 ExceptionHandler.java , 增加方法 test04

//如果发生了没有归类的异常, 可以给出统一提示页面
@RequestMapping(value = "/testException04")
public String test04(){
    String str = "hello";
    //这里会抛出 StringIndexOutOfBoundsException
    char c = str.charAt(10);
    return "success";
}

(3)创建/WEB-INF/pages/allEx.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>未知异常信息</title>
</head>
<body>
<h1>朋友,系统发生了未知异常~, 请联系网站管理员</h1>
</body>
</html>

(4)修改 test_exception.jsp

<a href="testException04">点击测试未知异常</a>

(5)启动服务器,测试效果

2.6 异常处理的优先级

**异常处理的优先级:**局部异常 > 全局异常 > SimpleMappingExceptionResolver > tomcat 默认机制

相关推荐
玉标6 分钟前
ELK日志系统和Filebeat采集器的学习总结
学习·elk
祖国的好青年11 分钟前
基于正点原子FreeRTOS学习笔记——时间片调度实验
笔记·学习
xiaoyalian43 分钟前
横截面数据回归
笔记·学习·数据挖掘·回归·r语言
王红花x1 小时前
STL——list模拟实现
c++·学习·list
家有狸花1 小时前
PYTHON自学笔记(一)vscode配置
笔记·vscode·python
Sam90291 小时前
《昇思 25 天学习打卡营第 11 天 | ResNet50 图像分类 》
学习·分类·数据挖掘
鸽芷咕1 小时前
【python学习】快速了解python基本数据类型
开发语言·python·学习
我是陈泽2 小时前
AI教你如何系统的学习Python
开发语言·数据结构·人工智能·python·学习·青少年编程
hello-alien2 小时前
ASP.NET Core----基础学习04----Model模型的创建 & 服务的注入
学习·c#·asp.net·model
细心的莽夫3 小时前
集合复习(java)
java·开发语言·笔记·学习·java-ee