一、拦截器的作用

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

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

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. Laya的List组件+滚动条

    版本:2.2.0 下面以<绯雨骑士团>的服务器选择列表为例子. 一 创建List 首先创建一个List组件,我命名为serverList. (不用像laya教程里那样,还要转换类型什么的, ...

  2. U盘无法格式化解决

    我的U盘是fat32格式的,4G以上的东西拖不进去 于是我打算格式化成NTFS的,然后悲剧了,格式化提示windows格式化不成功

  3. 阿里云composer 镜像

    2019年12月2日13:54:32 https://developer.aliyun.com/composer 阿里云的镜像更新时间比较及时 本镜像与 Packagist 官方实时同步,推荐使用最新 ...

  4. maven 国内镜像

    <mirrors> <!-- mirror | Specifies a repository mirror site to use instead of a given reposi ...

  5. 移动端1px边框解决方案

    在retina屏中,像素比为2(iPhone6/7/8)或3(iPhone6Plus/7Plus/8Plus),1px的边框看起来比真的1px更宽. 使用伪类加transform的方式 元素本身不定义 ...

  6. docker安装指定版本nexus3

    安装maven私服 1 下载指定版本的镜像  docker  pull  sonatype/nexus3:3.18.1 2 宿主机创建一个映射目录 ,并设置所有者 mkdir  -p  /app/ne ...

  7. [转帖]美团在Redis上踩过的一些坑-1.客户端周期性出现connect timeout

    美团在Redis上踩过的一些坑-1.客户端周期性出现connect timeout 博客分类: redis 运维 jedisconnect timeoutnosqltcp  转载请注明出处哈:http ...

  8. 【C++/C】指针基本用法简介-A Summary of basic usage of Pointers.

    基于谭浩强老师<C++程序设计(第三版)>做简要Summary.(2019-07-24) 一.数组与指针 1. 指针数组 一个数组,其元素均为指针类型数据,该数组称为指针数组.(type_ ...

  9. c++11多线程记录1 -- std::thread

    启动一个线程 话不多说,直接上代码 void func(); int main() { std::thread t(func); //这里就开始启动线程了 t.join(); // 必须调用join或 ...

  10. php数组到json的转变

    今天做项目遇到个问题,一个接口,输出二维数组,前端说他要的数据格式是数组,而不是对象,就像上个数据一样,我当时就懵逼了,,,什么对象?我明明输出的是数组啊...然后我看了看我返回的json串,emmm ...