「深入源码」Spring拦截器的实现原理和执行机制

我一直以为Spring的拦截器是基于AOP原理实现的,仔细阅读完源码,才知道自己的认知是错误的。
今天我们就从源码的角度剖析下Spring拦截器。

一.创建一个自己的拦截器

实现一个自己的拦截器,需要实现HandlerInterceptor接口。接口内的三个核心方法如下:

1.preHandle():该方法在业务处理器处理请求之前调用。

2.postHandle():这个方法在当前请求进行处理之后,也就是Controller方法调用之后执行,但是它会在DispatcherServlet 进行视图返回渲染之前被调用,所以我们可以在这个方法中对Controller处理之后的ModelAndView 对象进行操作。

3.afterCompletion():在postHandle执行之后执行,发生异常也会执行。该方法将在整个请求结束之后,也就是在DispatcherServlet渲染了对应的视图之后执行。这个方法的主要作用是用于进行资源清理工作的。

我们创建一个用户登录的拦截器:

@Component
public class MyInterceptor implements HandlerInterceptor {

    //在执行Controller方法之前来执行的
    //用于用户认证校验、用户权限校验
    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response, Object handler) throws Exception {

        //得到请求的url
        String url = request.getRequestURI();

        //判断是否是公开地址
        //实际开发中需要公开地址配置在配置文件中
        if(url.indexOf("login.action")>=0){
            //如果是公开地址则放行
            return true;
        }

        //判断用户身份在session中是否存在
        HttpSession session = request.getSession();
        String usercode = (String) session.getAttribute("usercode");
        //如果用户身份在session中存在放行
        if(usercode!=null){
            return true;
        }
        //执行到这里拦截,跳转到登陆页面,用户进行身份认证
        request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
        //如果返回false表示拦截不继续执行handler,如果返回true表示放行
        return false;
    }

    //在执行Controller方法之后返回modelAndView之前来执行
    //如果需要向页面提供一些公用的数据或配置一些视图信息,使用此方法实现从modelAndView入手
    @Override
    public void postHandle(HttpServletRequest request,
                          HttpServletResponse response, Object handler,
                          ModelAndView modelAndView) throws Exception {
        System.out.println("HandlerInterceptor...postHandle");
    }

    //完成对页面的渲染之后执行此方法
    //作系统统一异常处理,进行方法执行性能监控,在preHandle中设置一个时间点,在afterCompletion设置一个时间,两个时间点的差就是执行时长
    //实现系统统一日志记录
    @Override
    public void afterCompletion(HttpServletRequest request,
                               HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        System.out.println("HandlerInterceptor...afterCompletion");
    }
}
复制代码

二.拦截器的执行机制

我们知道SpringMVC框架的核心类为DispatchServlet,http请求的核心执行方法为

doService(),我们画下SpringMvc的工作流程图:

「深入源码」Spring拦截器的实现原理和执行机制

我们从流程图可以看到,拦截器的执行是在穿插在SpringMvc的工作流程中的,并没有用到动态代理机制,直接调用的拦截器方法。

拦截器在实现层面,并没有用到aop,并没有切面,通知这一类的代码,所以它的实现并不是基于aop的。但是拦截器从思想层面上,是面向切面编程的,是在controller这个层面上进行的代码织入。

三.拦截器如何添加到拦截器列表的

需要我们自己创建一个config,将拦截器添加进去。

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //注册TestInterceptor拦截器
        InterceptorRegistration registration = registry.addInterceptor(new MyInterceptor());
        registration.addPathPatterns("/**");                      //所有路径都被拦截
        registration.excludePathPatterns(                         //添加不拦截路径
                "你的登陆路径",            //登录
                "/**/*.html",            //html静态资源
                "/**/*.js",              //js静态资源
                "/**/*.css",             //css静态资源
                "/**/*.woff",
                "/**/*.ttf"
        );
    }
}
复制代码

在类WebMvcConfigurationSupport中会创建RequestMappingHandlerMapping

「深入源码」Spring拦截器的实现原理和执行机制

在创建RequestMappingHandlerMapping时,会将自定义的拦截器加入到拦截器列表中。

「深入源码」Spring拦截器的实现原理和执行机制

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享