用springboot复习springmvc的拦截器(interceptor)

一、怎么玩

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;
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,026评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,655评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,726评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,204评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,558评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,731评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,944评论 2 314
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,698评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,438评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,633评论 2 247
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,125评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,444评论 3 255
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,137评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,103评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,888评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,772评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,669评论 2 271

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,103评论 18 139
  • 姓名: 李小娜 [嵌牛导读]: SpringMVC 中的Interceptor 拦截器也是相当重要和相当有用的,...
    n184阅读 3,079评论 0 4
  • 转自:http://jinnianshilongnian.iteye.com/blog/1670856 处理器拦截...
    王帅199207阅读 575评论 0 6
  • 写在前面:因为部门项目中有用户登录验证方面的需求,故而学习了一下相关的验证技术,本文仅是作者个人学习的心得,由于水...
    xiangang阅读 8,956评论 2 14
  • 一 去年中秋回家,过完节要返回单位,一切收拾妥当就要出门了。父亲突然问我:“拿月饼不?”我想了一下,拿上怪沉的,但...
    雁南之阅读 467评论 0 1