一、拦截器的作用

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

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

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. 【转】Redis哨兵(Sentinel)模式

    主从切换技术的方法是:当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时间内服务不可用.这不是一种推荐的方式,更多时候,我们优先考虑哨兵模式. 一.哨兵 ...

  2. Gulp 搭建前端非SPA 项目, 修改文件浏览器自动刷新

    起因:需要搭建一个自动打包处理 sass / js (es6),自动监听文件变化时浏览器自动刷新的开发环境 项目目录 project build -css -js *.html src -html - ...

  3. Docker实践之04-操作容器

    目录 一.查看容器列表 二.启动容器 三.终止容器 四.重启容器 五.后台运行容器 六.获取容器输出信息 七.进入容器 八.导出和导入容器 九.删除容器 一.查看容器列表 可以使用命令docker c ...

  4. 【MongoDB学习之二】MongoDB数据库、文档、集合、元数据

    环境 MongoDB 4.0 CentOS6.5_x64 一.连接语法格式: mongodb://[username:password@]host1[:port1][,host2[:port2],.. ...

  5. activiti学习2:示例工程activiti-explorer.war的使用

    目录 activiti学习2:示例工程activiti-explorer.war的使用 一.搭建开发环境 二.运行示例工程 三.示例工程功能演示 1. 创建流程图 2. 部署流程图 3. 启动流程 4 ...

  6. OTP详解

    OTP(One Time Programmable)是单片机的一种存储器类型,意思是一次性可编程:程序烧入单片机后,将不可再次更改和清除. 随着嵌入式应用的越来越广泛,产品的安全也显得越来越重要.一方 ...

  7. 【记录】【MySQL】拼接字符串函数 CONCAT(str1,str2)

    CONCAT(str1,str2) 就是把str1和str2拼接

  8. phpmyadmin 显示被隐藏的表

    点击后,会把这个表隐藏掉.有时候误点会莫名其妙. 点击数据库上的眼睛,能够显示被隐藏的表.

  9. [Linux] - Manjaro ARM 系统配置(更新镜像源,安装 Docker 和 Dotnet Core)

    硬件:Raspberry Pi 4B系统:Manjaro-ARM-xfce-rpi4-19.08网址:https://manjaro.org/ Issue系统启动后,中文字符显示为小方格乱码 解决:安 ...

  10. linux终端命令行前缀设置为“当前目录”(非绝对路径)

    操作 打开家目录下的隐藏文件.bashrc 定位到62行,将小写的\W改为大写,保存即可. 重新打开bash 注意: /etc/profile , /etc/bashrc等文件里的环境变量设置会被.b ...