【Spring Boot】Spring 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);
                }
            }
        }
    }

原文地址:https://www.cnblogs.com/756623607-zhang/p/11259087.html

时间: 2024-10-26 12:05:08

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

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

1.首先编写拦截器代码 package com.sarnath.interceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.stereotype.Service; import org.springframework.web.servlet.HandlerInterceptor; import

Spring boot 自定义拦截器

1.新建一个类实现HandlerInterceptor接口,重写接口的方法 1 package com.zpark.interceptor; 2 3 import com.zpark.tools.Constants; 4 import com.zpark..tools.utils.EmptyUtils; 5 import org.slf4j.Logger; 6 import org.slf4j.LoggerFactory; 7 import org.slf4j.MDC; 8 import org

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

本文首发于个人网站:Spring Boot项目中如何定制拦截器 Servlet 过滤器属于Servlet API,和Spring关系不大.除了使用过滤器包装web请求,Spring MVC还提供HandlerInterceptor(拦截器)工具.根据文档,HandlerInterceptor的功能跟过滤器类似,但拦截器提供更精细的控制能力:在request被响应之前.request被响应之后.视图渲染之前以及request全部结束之后.我们不能通过拦截器修改request内容,但是可以通过抛出异

spring mvc &lt;mvc:annotation-driven/&gt; 自定义拦截器不走

<mvc:annotation-driven/> 这个便签会注册2个自定义拦截器,所以导致请求过来就会自己去走注册的这2个拦截器和定义的一堆bean 但是这个便签是必须得定义的 直接贴代码吧 <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context=&qu

Spring自定义拦截器

HandlerInterceptorAdapter由Spring MVC提供,用来拦截请求. 实现自定义拦截器需要继承HandlerInterceptorAdapter或实现HandlerInterceptor: preHandle()方法在业务处理器处理请求之前被调用  如果返回false  从当前的拦截器往回执行所有拦截器的afterCompletion(),再退出拦截器链 如果返回true,执行下一个拦截器,直到所有的拦截器都执行完毕,再执行被拦截的Controller,然后进入拦截器链,

Spring异步调用原理及SpringAop拦截器链原理

一.Spring异步调用底层原理 开启异步调用只需一个注解@EnableAsync @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(AsyncConfigurationSelector.class) public @interface EnableAsync { /** * Indicate the 'async' annotation type to be detected at

springMVC --拦截器流程详细,使用和自定义拦截器

先看看拦截器都做些什么: 1.日志记录:记录请求信息的日志,以便进行信息监控.信息统计.计算PV(PageView)等. 2.权限检查:如登录检测,进入处理器检测检测是否登录,如果没有直接返回到登录页面: 3.性能监控:有时候系统在某段时间莫名其妙的慢,可以通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间(如果有反向代理,如apache可以自动记录): 4.通用行为:读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取Loca

struts登录案例和自定义拦截器

struts登录案例:struts.xml<struts> <constant name="struts.devMode" value="true" /> <constant name="struts.custom.i18n.resources" value="messages"></constant> <package name="basic" ext

SpringMVC(四)-- 文件下载、自定义拦截器、异常处理

1.文件下载 用ResponseEntity<byte[]> 返回值完成文件下载 具体参见本博客之前的<文件上传下载> @RequestMapping(value="/testResponseEntity") public ResponseEntity<byte[]> testResponseEntity(HttpServletRequest request) throws Exception{ ServletContext servletConte