美文网首页
用springboot复习springmvc的拦截器(inter

用springboot复习springmvc的拦截器(inter

作者: 我的女友漏气了 | 来源:发表于2017-12-03 21:25 被阅读0次

    一、怎么玩

    1、引入pom.xml
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
    
    2、自定义拦截器
    //方式一
    public class InterceptorM1 implements HandlerInterceptor {
    
        private final Logger logger = LoggerFactory.getLogger(this.getClass().getCanonicalName());
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {
            logger.info("{}:在请求处理之前进行调用(Controller方法调用之前)", this.getClass().getSimpleName());
            return true;//只有返回true才会继续向下执行,返回false取消当前请求
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object o, ModelAndView modelAndView) throws Exception {
            logger.info("{}:请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)", this.getClass().getSimpleName());
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object o, Exception e) throws Exception {
            logger.info("{}:在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)", this.getClass().getSimpleName());
        }
    }
    
    //方式二
    public class InterceptorM2 extends HandlerInterceptorAdapter {
    
        private final Logger logger = LoggerFactory.getLogger(this.getClass().getCanonicalName());
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {
            logger.info("{}:在请求处理之前进行调用(Controller方法调用之前)", this.getClass().getSimpleName());
            return true;//只有返回true才会继续向下执行,返回false取消当前请求
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object o, ModelAndView modelAndView) throws Exception {
            logger.info("{}:请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)", this.getClass().getSimpleName());
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object o, Exception e) throws Exception {
            logger.info("{}:在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)", this.getClass().getSimpleName());
        }
    }
    
    3、JavaConfig形式的配置
    @Configuration
    public class WebMvcConfig extends WebMvcConfigurerAdapter{
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new InterceptorM1()).addPathPatterns("/**");//用于添加拦截规则
            registry.addInterceptor(new InterceptorM2()).addPathPatterns("/**");
            // 多个拦截器组成一个拦截器链
            // excludePathPatterns 用户排除拦截
        }
    }
    
    4、Controller模拟
    @RestController
    public class Rest {
    
        @GetMapping("/m1/{id}")
        public String m1(@PathVariable("id") String id) {
            return id;
        }
    
        @GetMapping("/m2/{id}")
        public String m2(@PathVariable("id") String id) {
            return id;
        }
    }
    
    5、项目启动

    二、嘛意思

    1、HandlerInterceptorAdapter 和 HandlerInterceptor 嘛关系
    public abstract class HandlerInterceptorAdapter implements AsyncHandlerInterceptor {
    
        /**
         * This implementation always returns {@code true}.这个实现总是返回true
             */
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                throws Exception {
    
            return true;
        }
    
        /**
         * This implementation is empty.这是一个空实现
         */
        @Override
        public void postHandle(
                HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
                throws Exception {
        }
    
        /**
         * This implementation is empty.这是一个空实现
         */
        @Override
        public void afterCompletion(
                HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
                throws Exception {
        }
    
        /**
         * This implementation is empty.这是一个空实现
         */
        @Override
        public void afterConcurrentHandlingStarted(
                HttpServletRequest request, HttpServletResponse response, Object handler)
                throws Exception {
        }
    
    }
    
    public interface AsyncHandlerInterceptor extends HandlerInterceptor {
    
        void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler)
                throws Exception;
    
    }
    
    public interface HandlerInterceptor {
    
    
        boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                throws Exception;
    
        
        void postHandle(
                HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
                throws Exception;
    
          
        void afterCompletion(
                HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
                throws Exception;
    }
    

    总结:HandlerInterceptorAdapter实现了HandlerInterceptor接口得子接口,那我们在开发时是实现接口HandlerInterceptor呢还是继承类HandlerInterceptorAdapter,当让了有的时候我们只是实现一个方法,那我们就继承类HandlerInterceptorAdapter,如果全部实现实现接口HandlerInterceptor

    2、拦截执行顺序
      a、正常流程(拦截器拦截到任何都成功)
     InterceptorM2--preHandle:在请求处理之前进行调用(Controller方法调用之前)
     Controller
     InterceptorM2--postHandle:请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)
     InterceptorM1--postHandle:请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)
     InterceptorM2--afterCompletion:在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)
     InterceptorM1--afterCompletion:在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)
    
      b、中断流程(M2失败了)
    InterceptorM1--preHandle:在请求处理之前进行调用(Controller方法调用之前)
    InterceptorM2--preHandle:在请求处理之前进行调用(Controller方法调用之前)
    InterceptorM1--afterCompletion:在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)
    

    如果有3个拦截器第2个中断,第三个就不执行了。

    3、源码学习
    public class HandlerExecutionChain {
       /**
         * Apply preHandle methods of registered interceptors.
         * @return {@code true} if the execution chain should proceed with the
         * next interceptor or the handler itself. Else, DispatcherServlet assumes
         * that this interceptor has already dealt with the response itself.
         */
        boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
            HandlerInterceptor[] interceptors = getInterceptors();
            if (!ObjectUtils.isEmpty(interceptors)) {
                for (int i = 0; i < interceptors.length; i++) {
                    HandlerInterceptor interceptor = interceptors[i];
                 // interceptorIndex默认是-1,如果n 个拦截器失败了,就n-1执行triggerAfterCompletion(request, response, null);
                    if (!interceptor.preHandle(request, response, this.handler)) {
                        triggerAfterCompletion(request, response, null);
                        return false;
                    }
                    this.interceptorIndex = i;
                }
            }
            return true;
        }
             /**
         * Apply postHandle methods of registered interceptors.
         */
        void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
                throws Exception {
    
            HandlerInterceptor[] interceptors = getInterceptors();
            if (!ObjectUtils.isEmpty(interceptors)) {
                for (int i = interceptors.length - 1; i >= 0; i--) {
                    HandlerInterceptor interceptor = interceptors[i];
                    interceptor.postHandle(request, response, this.handler, mv);
                }
            }
        }
    
        /**
         * Trigger afterCompletion callbacks on the mapped HandlerInterceptors.
         * Will just invoke afterCompletion for all interceptors whose preHandle invocation
         * has successfully completed and returned true.
         */
        void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)
                throws Exception {
    
            HandlerInterceptor[] interceptors = getInterceptors();
            if (!ObjectUtils.isEmpty(interceptors)) {
                for (int i = this.interceptorIndex; i >= 0; i--) {
                    HandlerInterceptor interceptor = interceptors[i];
                    try {
                        interceptor.afterCompletion(request, response, this.handler, ex);
                    }
                    catch (Throwable ex2) {
                        logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
                    }
                }
            }
        }
    
        /**
         * Apply afterConcurrentHandlerStarted callback on mapped AsyncHandlerInterceptors.
         */
        void applyAfterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response) {
            HandlerInterceptor[] interceptors = getInterceptors();
            if (!ObjectUtils.isEmpty(interceptors)) {
                for (int i = interceptors.length - 1; i >= 0; i--) {
                    if (interceptors[i] instanceof AsyncHandlerInterceptor) {
                        try {
                            AsyncHandlerInterceptor asyncInterceptor = (AsyncHandlerInterceptor) interceptors[i];
                            asyncInterceptor.afterConcurrentHandlingStarted(request, response, this.handler);
                        }
                        catch (Throwable ex) {
                            logger.error("Interceptor [" + interceptors[i] + "] failed in afterConcurrentHandlingStarted", ex);
                        }
                    }
                }
            }
        }
      
    }
    

    三、干嘛用

    1、性能监控 (kaitao)
    /**
     * 性能监控
     */
    public class PermHandlerInterceptor extends HandlerInterceptorAdapter {
    
        private final Logger logger = LoggerFactory.getLogger(this.getClass().getCanonicalName());
        //Spring提供的一个命名的ThreadLocal实现
        private NamedThreadLocal<Long> currentTimeThreadLocal = new NamedThreadLocal<>("startTime");
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            PermIgnore annotation;
            if (handler instanceof HandlerMethod){
                annotation = ((HandlerMethod) handler).getMethodAnnotation(PermIgnore.class);
            }else {
                return true;
            }
            //如果有PermIgnore注解,则不性能检测
            if (annotation != null) {
                return true;
            }
            long startTime = System.currentTimeMillis();
            currentTimeThreadLocal.set(startTime);
    
            return true;
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    
            PermIgnore annotation;
            if (handler instanceof HandlerMethod){
                annotation = ((HandlerMethod) handler).getMethodAnnotation(PermIgnore.class);
            }else {
                return;
            }
            //如果有PermIgnore注解,则不性能检测
            if (annotation != null) {
                return;
            }
            long endTime = System.currentTimeMillis();
            long startTime = currentTimeThreadLocal.get();
            long useTime = endTime - startTime;
            if (useTime > 1) {
                logger.info("[{}] use {} ms]", request.getRequestURI(), useTime);
            }
        }
    }
    
    /**
     * 忽略性能监控,默认有
     */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface PermIgnore {
    }
    
    @RestController
    public class Rest {
    
        private final Logger logger = LoggerFactory.getLogger(this.getClass().getCanonicalName());
    
        @GetMapping("/api/{id}")
        @PermIgnore
        public String m1(@PathVariable("id") String id) {
            logger.info("{}", this.getClass().getSimpleName());
            return id;
        }
    
        @GetMapping("/api2/{id}")
        public String m2(@PathVariable("id") String id) {
            logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
            return id;
        }
    
    }
    
    m2
    [/api2/2] use 53 ms]
    Rest
    
    2、登录检测(renren-fastplus)
    @Component
    public class AuthorizationInterceptor extends HandlerInterceptorAdapter {
        @Autowired
        private SysUserTokenService tokenService;
    
        public static final String USER_KEY = "userId";
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            AuthIgnore annotation;
            if (handler instanceof HandlerMethod) {
                annotation = ((HandlerMethod) handler).getMethodAnnotation(AuthIgnore.class);
            } else {
                return true;
            }
    
            //如果有@IgnoreAuth注解,则不验证token
            if (annotation != null) {
                return true;
            }
    
            //从header中获取token
            String token = request.getHeader("token");
            //如果header中不存在token,则从参数中获取token
            if (StringUtils.isBlank(token)) {
                token = request.getParameter("token");
            }
    
            //token为空
            if (StringUtils.isBlank(token)) {
                throw new RRException("token不能为空");
            }
    
            //查询token信息
            SysUserTokenEntity tokenEntity = tokenService.queryByToken(token);
            if (tokenEntity == null || tokenEntity.getExpireTime().getTime() < System.currentTimeMillis()) {
                throw new RRException("token失效,请重新登录", 520);
            }
    
            //设置userId到request里,后续根据userId,获取用户信息
            request.setAttribute(USER_KEY, tokenEntity.getUserId());
    
            return true;
        }
    }
    

    相关文章

      网友评论

          本文标题:用springboot复习springmvc的拦截器(inter

          本文链接:https://www.haomeiwen.com/subject/rdaabxtx.html