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);
    

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);

 

以上是关于Spring BootSpring Boot之自定义拦截器的主要内容,如果未能解决你的问题,请参考以下文章

Spring BootSpring Boot之使用ImportBeanDefinitionRegistrar类实现动态注册Bean

Spring BootSpring Boot之两种引入spring boot maven依赖的方式

spring bootspring boot @ConditionalOnxxx相关注解总结

Spring BootSpring Boot项目部署到外部Tomcat容器

Spring BootSpring Boot之自定义拦截器

spring bootspring cloud下spring boot微服务启动没有报错,但是访问访问不到