SpringBoot HandlerInterceptor 拦截器

环境

  • SpringBoot 环境

相关

  • spring-boot在web层面使用了spring mvc的拦截器功能,并没有做其他处理,故我们只要熟悉mvc的拦截器,自然而然可以将拦截器加入到spring-boot上
  • 已知request 请求处理顺序,Filter -> HandlerInterceptor 拦截器 -> AOP
  • Filter 过滤器属于 Servlet 范畴的API, 与Spring 没什么关系
  • HandlerInterceptor 拦截器 属于Spring 的范畴
  • 只有经过 dispatchservlet 的请求,才会走拦截器chain,我们自定义的的servlet 请求是不会被拦截的
  • 但过滤器会拦截所有请求

拦截器提供更精细的控制-时间段区分

  • 在request被controller 响应之前,只有返回true 才会进入后续interceptor 和 controller
  • 在request被响应之后-dispatcherServlet进行视图渲染之前,可以在此处对Controller 处理后的ModelAndView对象进行处理
  • 视图渲染之后,用于资源清理

特点

我们不能通过修改拦截器修改request内容,但可以通过抛出异常(或者返回false)暂停request执行

案例

基础
Spring MVC 中拦截器顶层是 HandlerInterceptor, 它有3个方法

public interface HandlerInterceptor {

    //controller 方法执行前执行,当return TRUE 时进入下一个 拦截器 或直接进入 Controller 方法
    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 抽象类 来实现自定义拦截器

public abstract class HandlerInterceptorAdapter implements AsyncHandlerInterceptor {

    /**
     * This implementation always returns  true
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {

        return true; //硬编码为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 {
    }

}

AsyncHandlerInterceptor接口代码

public interface AsyncHandlerInterceptor extends HandlerInterceptor {

    /**
     * Called instead of {@code postHandle} and {@code afterCompletion}, when
     * the a handler is being executed concurrently.
     * <p>Implementations may use the provided request and response but should
     * avoid modifying them in ways that would conflict with the concurrent
     * execution of the handler. A typical use of this method would be to
     * clean up thread-local variables.
     * @param request the current request
     * @param response the current response
     * @param handler the handler (or {@link HandlerMethod}) that started async
     * execution, for type and/or instance examination
     * @throws Exception in case of errors
     */
    void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception;

}

  • HandlerInterceptorAdapter 是 适配器 模式的一种
    • 实现适配器模式有两种方式:继承 和 组合
  • 实现接口时,可以将某些接口方法写死,然后让继承类覆盖
  • AsyncHandlerInterceptor 接口只增添了一个接口,这种设计模式可以留意下

自定义拦截器

@Slf4j
public class MyHandlerInterceptor1 extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("handler interceptor1 preHandle");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("handler interceptor1 postHandle");
        //super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("handler interceptor1 afterComplete");
        //super.afterCompletion(request, response, handler, ex);
    }
}
@Slf4j
public class MyHandlerInterceptor2 extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("handler interceptor2 preHandle");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("handler interceptor2 postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("handler interceptor2 afterComplete");
    }
}

注册拦截器

@Configuration //标记为配置类,本质上是将该类纳入上下文从而执行addInterceptor动作,故@Component注解亦可
public class MyInterceptorConfigure extends WebMvcConfigurerAdapter {
    //继承WebMVCConfigureAdapter,并覆盖 addInterceptror 方法
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyHandlerInterceptor1());
        registry.addInterceptor(new MyHandlerInterceptor2()); //添加顺序决定了拦截顺序
        super.addInterceptors(registry);
    }
}

controller

@RestController
@RequestMapping("/interceptor")
public class MyInterceptorController {

    @RequestMapping("") //没有主method属性,表示接受任意method请求
    public String testIntercept(String param) {  //未加 @RequestParam() 同样也可绑定方法参数param 
        System.out.println("param: " + param);
        return param;
    }

}

运行结果

2018-01-17 01:16:50.993  INFO 568 --- [           main] c.l.s.interceptor.MyHandlerInterceptor1  : handler interceptor1 preHandle
2018-01-17 01:16:50.993  INFO 568 --- [           main] c.l.s.interceptor.MyHandlerInterceptor2  : handler interceptor2 preHandle
param: test
2018-01-17 01:16:51.029  INFO 568 --- [           main] c.l.s.interceptor.MyHandlerInterceptor2  : handler interceptor2 postHandle
2018-01-17 01:16:51.030  INFO 568 --- [           main] c.l.s.interceptor.MyHandlerInterceptor1  : handler interceptor1 postHandle
2018-01-17 01:16:51.032  INFO 568 --- [           main] c.l.s.interceptor.MyHandlerInterceptor2  : handler interceptor2 afterComplete
2018-01-17 01:16:51.032  INFO 568 --- [           main] c.l.s.interceptor.MyHandlerInterceptor1  : handler interceptor1 afterComplete

添加Aspect后的运行结果
Aspect 代码

@Component
@Aspect
@Slf4j
public class TestAspect {

    @Pointcut(("execution(public * com.lance.spring.controller.*.*(..))"))
    public void restPoint() {

    }

    @Before("restPoint()")
    public void before(JoinPoint joinPoint) {
        log.info("aspect before pointcut");
    }

    @After("restPoint()")
    public void after(JoinPoint joinPoint) {
        log.info("aspect after pointcut");
    }

    @Around("restPoint()")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("aspect around before target method");
        joinPoint.proceed();
        log.info("aspect around after target method");
    }
}

结果

2018-01-17 01:19:42.860  INFO 6256 --- [           main] c.l.s.interceptor.MyHandlerInterceptor1  : handler interceptor1 preHandle
2018-01-17 01:19:42.860  INFO 6256 --- [           main] c.l.s.interceptor.MyHandlerInterceptor2  : handler interceptor2 preHandle
2018-01-17 01:19:42.889  INFO 6256 --- [           main] com.lance.spring.aop.TestAspect          : aspect around before target method
2018-01-17 01:19:42.889  INFO 6256 --- [           main] com.lance.spring.aop.TestAspect          : aspect before pointcut
param: test
2018-01-17 01:19:42.899  INFO 6256 --- [           main] com.lance.spring.aop.TestAspect          : aspect around after target method
2018-01-17 01:19:42.899  INFO 6256 --- [           main] com.lance.spring.aop.TestAspect          : aspect after pointcut
2018-01-17 01:19:42.915  INFO 6256 --- [           main] c.l.s.interceptor.MyHandlerInterceptor2  : handler interceptor2 postHandle
2018-01-17 01:19:42.916  INFO 6256 --- [           main] c.l.s.interceptor.MyHandlerInterceptor1  : handler interceptor1 postHandle
2018-01-17 01:19:42.916  INFO 6256 --- [           main] c.l.s.interceptor.MyHandlerInterceptor2  : handler interceptor2 afterComplete
2018-01-17 01:19:42.916  INFO 6256 --- [           main] c.l.s.interceptor.MyHandlerInterceptor1  : handler interceptor1 afterComplete
参考:http://jinnianshilongnian.iteye.com/blog/1675504
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,277评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,689评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,624评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,356评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,402评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,292评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,135评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,992评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,429评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,636评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,785评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,492评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,092评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,723评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,858评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,891评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,713评论 2 354

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,654评论 18 139
  • 姓名: 李小娜 [嵌牛导读]: SpringMVC 中的Interceptor 拦截器也是相当重要和相当有用的,...
    n184阅读 3,162评论 0 4
  • 1、Spring MVC请求流程 (1)初始化:(对DispatcherServlet和ContextLoderL...
    拾壹北阅读 1,948评论 0 12
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,809评论 6 342
  • 你托三月的风捎来 春的消息 柔声细语的温柔 和煦是你的眼眸 你命七月的雨冷却 夏的狂热 冰与火的交织里 你纷扬的衣...
    岁往阅读 342评论 0 1