一、拦截器的作用

  将通用的代码抽取出来,达到复用的效果。比如可以用来做日志记录、登录判断、权限校验等等

二、如何实现自定义拦截器

1)创建自定义拦截器类并实现HandlerInterceptor类

/**
* @author zhangboqing
* @date 2019-07-28
*/
public class MyInterceptor implements HandlerInterceptor { /**
* 执行Controller方法之前,调用
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //返回false,请求将被拦截。放回true代表放行
return false;
} /**
* 执行Controller方法之后,响应给前端之前,调用
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } /**
* 响应给前端之后,调用
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { }
}

2)将我们自已的拦截器注册到注册器中

/**
* @author zhangboqing
* @date 2019-07-28
*
* MVC配置类
*/
@Configuration
public class MyWebMvcConfigurer implements WebMvcConfigurer { /** 创建自定义拦截器实例 */
@Bean
MyInterceptor myInterceptor() {
return new MyInterceptor();
} @Override
public void addInterceptors(InterceptorRegistry registry) { //注册自定义拦截器
registry.addInterceptor(myInterceptor())
//拦截所有请求
.addPathPatterns("/*")
//指定需要过滤的请求地址
// .excludePathPatterns()
;
}
}

三、请求日志记录拦截器实现

import com.alibaba.fastjson.JSON;
import com.talkilla.talkillalexile.common.utils.JodaTimeUtils;
import com.talkilla.talkillalexile.common.utils.RandomCodeUtils;
import com.talkilla.talkillalexile.config.filter.HttpHelperUtils;
import com.talkilla.talkillalexile.config.interceptor.model.PostRequestLogInfoModel;
import com.talkilla.talkillalexile.config.interceptor.model.PreRequestLogInfoModel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map; /**
* @author zhangboqing
* @date 2018/10/13
* <p>
* 日志打印拦截
*/
@Slf4j
public class LoggingInterceptor implements HandlerInterceptor { @Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//生成这次请求的唯一标识
String requestUUID = RandomCodeUtils.getUUID();
long logStartTime = System.currentTimeMillis(); //记录开始时间
request.setAttribute("logStartTime", logStartTime);
request.setAttribute("requestUUID",requestUUID); //请求日志记录
preRequestLoggin(request,requestUUID);
return true;
} @Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
long logEndTime = System.currentTimeMillis();
long logStartTime = (Long)request.getAttribute("logStartTime");
String requestUUID = (String)request.getAttribute("requestUUID"); //记录整个请求的执行时间
loggingHandleTime(requestUUID,logStartTime,logEndTime);
} private void loggingHandleTime(String requestUUID, long logStartTime, long logEndTime) {
String logInfo = getLoggingHandleTime(requestUUID,logStartTime,logEndTime);
log.info("[请求拦截日志信息]:{}", logInfo);
} private String getLoggingHandleTime(String requestUUID, long logStartTime, long logEndTime) {
PostRequestLogInfoModel build = PostRequestLogInfoModel.builder()
.requestUUID(requestUUID)
.requestTime(JodaTimeUtils.timestampToString(logStartTime / 1000, JodaTimeUtils.DateFormat.DATETIME_FORMAT))
.responseTime(JodaTimeUtils.timestampToString(logEndTime / 1000, JodaTimeUtils.DateFormat.DATETIME_FORMAT))
.handleTime((logEndTime - logStartTime) + "ms").build(); return JSON.toJSONString(build);
} /**
* 请求日志记录
*
* @param request
*/
private void preRequestLoggin(HttpServletRequest request,String requestUUID) {
//获取相关参数
//请求地址
String requestURI = request.getRequestURI();
//请求方法
String method = request.getMethod();
//请求参数
Map<String, String[]> parameterMap = request.getParameterMap();
String bodyString = "";
try {
bodyString = HttpHelperUtils.getBodyString(request);
} catch (IOException e) {
e.printStackTrace();
} String reqestLogInfo = getRequestLogInfo(requestURI, method, parameterMap, bodyString,requestUUID);
log.info("[请求拦截日志信息]:{}", reqestLogInfo);
} private String getRequestLogInfo(String requestURI, String method, Map<String, String[]> getParameterMap, String postBodyString,String requestUUID) { PreRequestLogInfoModel build = PreRequestLogInfoModel.builder()
.requestUUID(requestUUID)
.requestURI(requestURI)
.method(method)
.getParameter(getParameterMap)
.postParameter(postBodyString).build(); return JSON.toJSONString(build);
} }

四、从源码角度去理解拦截器三个方法的执行时机

将代码定位到Spring MVC核心处理类DispatcherServlet的doDispatcher()方法,从标记的1,2,3,4,5可以很清楚的看出下面几点启示

  1.启示一:拦截器的preHandle方法是在执行Controller方法之前被调用的

  2.启示二:拦截器的postHandle方法是在执行Controller方法之后被调用的,但是再处理响应结果之前

    3.启示三:拦截器的afterCompletion方法是在处理响应结果之后执行的,也就是说,在调用afterCompletion方法的时候,响应结果已经返回给前端了,该方法的任何处理都不会影响响应结果

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try {
ModelAndView mv = null;
Exception dispatchException = null; try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request); // Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
} // Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
} // 1.执行拦截器preHandle()方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
} // 2.实际调用处理程序(Controller的方法)
mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) {
return;
} applyDefaultViewName(processedRequest, mv);
          // 3.执行拦截器postHandle()方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
        // 4.处理响应结果
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
// 5.执行拦截器afterCompletion()方法
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}

【Spring Boot】Spring Boot之自定义拦截器的更多相关文章

  1. JavaEE开发之SpringMVC中的自定义拦截器及异常处理

    上篇博客我们聊了<JavaEE开发之SpringMVC中的路由配置及参数传递详解>,本篇博客我们就聊一下自定义拦截器的实现.以及使用ModelAndView对象将Controller的值加 ...

  2. 【第四十章】Spring Boot 自定义拦截器

    1.首先编写拦截器代码 package com.sarnath.interceptor; import javax.servlet.http.HttpServletRequest; import ja ...

  3. Spring boot 自定义拦截器

    1.新建一个类实现HandlerInterceptor接口,重写接口的方法 package com.zpark.interceptor; import com.zpark.tools.Constant ...

  4. Spring Boot 2.X 如何添加拦截器?

    最近使用SpringBoot2.X搭建了一个项目,大部分接口都需要做登录校验,所以打算使用注解+拦截器来实现,在此记录下实现过程. 一.实现原理 1. 自定义一个注解@NeedLogin,如果接口需要 ...

  5. Spring Boot干货:静态资源和拦截器处理

    前言 本章我们来介绍下SpringBoot对静态资源的支持以及很重要的一个类WebMvcConfigurerAdapter. 正文 前面章节我们也有简单介绍过SpringBoot中对静态资源的默认支持 ...

  6. Spring Boot项目中如何定制拦截器

    本文首发于个人网站:Spring Boot项目中如何定制拦截器 Servlet 过滤器属于Servlet API,和Spring关系不大.除了使用过滤器包装web请求,Spring MVC还提供Han ...

  7. Spring MVC中自定义拦截器的简单示例

    1. 引言 拦截器(Interceptor)实现对每一个请求处理前后进行相关的业务处理,类似于Servlet的Filter. 我们可以让普通的Bean实现HandlerIntercpetor接口或继承 ...

  8. spring mvc <mvc:annotation-driven/> 自定义拦截器不走

    <mvc:annotation-driven/> 这个便签会注册2个自定义拦截器,所以导致请求过来就会自己去走注册的这2个拦截器和定义的一堆bean 但是这个便签是必须得定义的 直接贴代码 ...

  9. Spring自定义拦截器

    HandlerInterceptorAdapter由Spring MVC提供,用来拦截请求. 实现自定义拦截器需要继承HandlerInterceptorAdapter或实现HandlerInterc ...

随机推荐

  1. EasyDSS高性能RTMP、HLS(m3u8)、HTTP-FLV、RTSP流媒体服务器软件对数据库Sqlite3和MySQL的支持说明

    背景分析 EasyDSS商用流媒体服务器提供一站式的转码.点播.直播.时移回放服务,极大地简化了开发和集成的工作,并且EasyDSS支持多种特性,完全能够满足企业视频信息化建设方面的需求.其中,点播功 ...

  2. Apache限制IP并发数和流量控制

    使用mod_limitipconn模块限制IP并发连接数安装: wget http://dominia.org/djao/limit/mod_limitipconn-0.24.tar.bz2 tar ...

  3. 简单工厂(二)——coding

    public abstract class Video { public abstract void produce(); } public class JavaVideo extends Video ...

  4. 【视频开发】opencv不能读取MP4格式文件

    用OpenCV对视频进行处理时,不能打开.mp4格式的文件.后来解决办法是用ffmpeg.exe将mp4文件转码为avi文件,然后再进行处理.但是转码过程比较慢.后来查看opencv源代码,发现它也是 ...

  5. Linux 就该这么学 CH04 VIM编辑器和Shell命令脚本

    0 概述 1 Vim编辑器 在linux 中一切都是文件,而配置一个服务就是修改其配置文件的参数. vim 编辑器有三种模式:命令模式,末行模式和编辑模式. 命令模式:控制光标移动,对文件进行操作. ...

  6. Dede后台验证码不显示解决方法详解(dedecms 5.7 UTF-8版本)

    织梦(dede)后台验证码不显示有多种可能性,我前几天测试就碰到了这个问题,结果百度搜索了一圈,挨个修改了下,还是不行,最后是在解决另外一个后台上传图片不成功的问题的时候,歪打正着的把验证码问题给解决 ...

  7. MySQL Community Server 8.0.16

    1 首先 我们需要先下载一个 Mysql 点击这个网址进入 Mysql 的官网的下载地址: https://dev.mysql.com/downloads/mysql/ 首先 根据你的电脑的操作系统选 ...

  8. 【剑指offer】平衡二叉树

    题目描述 输入一棵二叉树,判断该二叉树是否是平衡二叉树. 分析:采用后序遍历的方式判断左右子树的高度差是否大于1 class Solution { public: bool flag; int f(T ...

  9. Vue(七)整合vue-router&Vuex&Axios

    整合vue-router&Vuex 先创建工程 vue create vue-axios 然后选择 勾选 回车,出现是否使用history mode?选择y,代表URL地址里面不会出现#.选择 ...

  10. MariaDB 10.3 序列

    在MariaDB .3版本中sequence是特殊的表,和表使用相同的namespace,因此表和序列的名字不能相同. MariaDB [wuhan]> select version(); +- ...