zoukankan      html  css  js  c++  java
  • 【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);
        }
    
    }
    View Code

    四、从源码角度去理解拦截器三个方法的执行时机

    将代码定位到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); } } } }
  • 相关阅读:
    React在componentDidMount里面发送请求
    React 术语词汇表
    React里受控与非受控组件
    React和Vue等框架什么时候操作DOM
    【LeetCode】79. Word Search
    【LeetCode】91. Decode Ways
    【LeetCode】80. Remove Duplicates from Sorted Array II (2 solutions)
    【LeetCode】1. Two Sum
    【LeetCode】141. Linked List Cycle (2 solutions)
    【LeetCode】120. Triangle (3 solutions)
  • 原文地址:https://www.cnblogs.com/756623607-zhang/p/11259087.html
Copyright © 2011-2022 走看看