一、拦截器的作用

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

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

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. Spring IoC 的实现机制

    Spring 中的 IoC 的实现原理就是工厂模式加反射机制. interface Fruit {     public abstract void eat(); } class Apple impl ...

  2. start & stop kafka cluster shell script

    kafka_start_cluster.sh #!/bin/bash brokers="kafka-server-1 kafka-server-2 kafka-server-3" ...

  3. bladex-boot推送harbor出错

    出错信息: Building image harbor.zhangshuiqing.com:8081/blade/Bladex-boot:2.2.1.release十二月 13, 2019 11:22 ...

  4. npm package

    lodash JavaScript 实用工具库,提供一致性,模块化,性能和配件等功能 用npm-run自动化任务 Express 4.x API 中文手册 Handlebars.js 模板引擎 使用n ...

  5. 无限用teamviewer的一种方法,虚拟机中装teamviewer

    可以使用window 多用户可以同时远程登陆的特性(win服务器版自动支持多用户同时远程登陆,非服务器版需要其他方法开启) 1.在window建立两个用户,如a,b. 2安装虚拟机vmware,安装操 ...

  6. Install Teamviewer on 14.04? [repost]

    Ref: http://askubuntu.com/questions/453157/how-to-install-teamviewer-on-14-04 TeamViewer 是一款优秀的跨平台免费 ...

  7. GitHub: Oracle Database on Docker 为测试 改天试试

    Oracle Database on Docker https://github.com/oracle/docker-images/tree/master/OracleDatabase/SingleI ...

  8. 微信小程序获取微信绑定的手机号

    怎么获取微信绑定手机号呢?我们授权登录的时候,我们只能获取微信登录人员的头像,昵称,性别之类的,而手机号需要二次授权才可以,那么获取手机号都需要哪些条件呢?来看官方文档 获取手机号 获取微信用户绑定的 ...

  9. HLP帮助文件源文件RTF文件的编写

    https://www.cnblogs.com/gaodu2003/archive/2008/12/17/1356861.html 举例说明如下: 每一节的标题在RTF文件中一般以特有的脚注($)指定 ...

  10. C语言合并果子-贪心算法

    /*有几堆水果.每次你把两堆东西移到一起,形成更大的一堆.每个动作消耗的能量是两堆水果的总重量.如何把所有的水果堆在一起,消耗最少的能量?*/ 以上是题目,该题首先要读懂题目,每次移到一起以后都要将数 ...