Spring BootSpring Boot之自定义拦截器
Posted 756623607-zhang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring BootSpring Boot之自定义拦截器相关的知识,希望对你有一定的参考价值。
一、拦截器的作用
将通用的代码抽取出来,达到复用的效果。比如可以用来做日志记录、登录判断、权限校验等等
二、如何实现自定义拦截器
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 BootSpring Boot之自定义拦截器的主要内容,如果未能解决你的问题,请参考以下文章
Spring BootSpring Boot之使用ImportBeanDefinitionRegistrar类实现动态注册Bean
Spring BootSpring Boot之两种引入spring boot maven依赖的方式
spring bootspring boot @ConditionalOnxxx相关注解总结