当前位置: 首页 > news >正文

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

自定义拦截器

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.这里根据业务,可以进行拦截,并指定跳转到哪个页面*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("--MyInterceptor01--preHandle()--");return true;}/*** 1. 在目标方法执行后,会执行postHandle()* 2.*/@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("--MyInterceptor01--postHandle()--");}/*** 1. 该方法在视图渲染后被执行,这里可以进行资源清理*/@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("--MyInterceptor01--afterCompletion()--");}
}

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

<!--配置自定义拦截器-->
<mvc:interceptors><!--1 第一种配置方式,直接使用ref 引用到对应的myInterceptor012 这种方式,会拦截所有的目标方法--><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 {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("--MyInterceptor02--preHandle()--");return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("--MyInterceptor02--postHandle()--");}@Overridepublic 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};//抛出一个数组越界的异常 ArrayIndexOutOfBoundsExceptionSystem.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";//这里会抛出 StringIndexOutOfBoundsExceptionchar 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 默认机制 

 

相关文章:

  • 第二十五篇——信息加密:韦小宝说谎的秘诀
  • R可视化:微生物相对丰度或富集热图可视化
  • RISC_CPU模块的调试
  • 开发一个python工具,pdf转图片,并且截成单个图片,然后修整没用的白边
  • MyBatis打印不带问号SQL
  • 电子书(chm)-加载JS--CS上线
  • 鸿蒙开发组件:【FA模型的Context】
  • 【html5的video标签在移动端的使用】【微信内部浏览器video自动播放】【vue-video-player】
  • 用ip link add link命令创建vlan子设备
  • 【踩坑】修复Ubuntu远程桌面忽然无法Ctrl C/V复制粘贴及黑屏
  • VMware清理拖拽缓存
  • Avalonia for VSCode
  • 电脑有线无线一起用怎么设置
  • 宕机了, redis如何保证数据不丢?
  • 【转载】使用 .NET Upgrade Assistant(升级助手)升级 .NET 老旧版本项目
  • 10个确保微服务与容器安全的最佳实践
  • 3.7、@ResponseBody 和 @RestController
  • HashMap剖析之内部结构
  • Iterator 和 for...of 循环
  • java多线程
  • node-sass 安装卡在 node scripts/install.js 解决办法
  • October CMS - 快速入门 9 Images And Galleries
  • Redis的resp协议
  • vue 个人积累(使用工具,组件)
  • 阿里云容器服务区块链解决方案全新升级 支持Hyperledger Fabric v1.1
  • 第2章 网络文档
  • 短视频宝贝=慢?阿里巴巴工程师这样秒开短视频
  • - 概述 - 《设计模式(极简c++版)》
  • 入口文件开始,分析Vue源码实现
  • 设计模式 开闭原则
  • 使用iElevator.js模拟segmentfault的文章标题导航
  • ​虚拟化系列介绍(十)
  • (04)Hive的相关概念——order by 、sort by、distribute by 、cluster by
  • (搬运以学习)flask 上下文的实现
  • (备份) esp32 GPIO
  • (带教程)商业版SEO关键词按天计费系统:关键词排名优化、代理服务、手机自适应及搭建教程
  • (二)fiber的基本认识
  • (二十四)Flask之flask-session组件
  • (淘宝无限适配)手机端rem布局详解(转载非原创)
  • (转)菜鸟学数据库(三)——存储过程
  • *setTimeout实现text输入在用户停顿时才调用事件!*
  • .mkp勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复
  • .NET DataGridView数据绑定说明
  • .NET MVC 验证码
  • .NET 设计模式初探
  • .Net 执行Linux下多行shell命令方法
  • .NET/C# 利用 Walterlv.WeakEvents 高性能地定义和使用弱事件
  • .NET学习全景图
  • @DS 多数据源 + @Transactional(rollbackFor = Exception.class) 导致@DS 多数据源没法使用
  • [ 网络通信基础 ]——网络的传输介质(双绞线,光纤,标准,线序)
  • [16/N]论得趣
  • [20180129]bash显示path环境变量.txt
  • [C/C++入门][字符与ASCII码]6、用代码来转换字符与它的ASCII码
  • [C++随笔录] 红黑树
  • [codeforces] 25E Test || hash