饿了么急招岗位:说说 Spring 拦截链的原理!

开发 前端
本文,我们深入探讨了 Spring拦截链的实现原理和源码分析,不仅可以帮助我们更好地使用 Spring提供的拦截器功能,还能让我们在需要时自定义复杂的拦截逻辑,增强应用的灵活性和可维护性。​

这两天,有小伙伴面了饿了么后端急招岗位,题目:说说 Spring拦截链的工作原理?结果他急眼了...

这篇文章,我们帮这个小伙伴扳回一局,从概念到原理,再到核心源码,里里外外把 Spring拦截链的原理讲个透,来,开干!

1. 什么是拦截链?

简单来说,拦截链是一个处理请求的拦截器列表,按照一定的顺序,一个一个地拦截并处理请求。每个拦截器都可以在请求处理前、处理后或完成后执行一些逻辑。

比如,我们可能需要在所有请求处理前进行权限验证,在处理后记录日志,或者在请求完成后释放资源。这些操作可以通过定义不同的拦截器来实现,每个拦截器负责一个特定的任务。

2. Spring中的拦截链

在 Spring MVC中,拦截器链是通过HandlerInterceptor接口及其实现类来实现的。Spring的DispatcherServlet作为前端控制器(Front Controller),负责协调请求的各个阶段,包括调用拦截器。

拦截器链的实现允许多个拦截器按照一定的顺序对请求进行处理。每个拦截器都有机会在请求处理前后执行特定的逻辑,这为我们在请求处理流程中插入自定义逻辑提供了极大的灵活性。

3. 拦截链的核心组件

要理解拦截链的实现原理,首先需要了解 Spring MVC 中 4个核心组件的作用和互相之间的关系:

  • HandlerMapping:HandlerMapping负责将请求URL映射到具体的处理器(Handler)。处理器通常是一个控制器(Controller)的方法。Spring提供了多种HandlerMapping实现,如RequestMappingHandlerMapping,支持基于注解的映射。
  • HandlerAdapter:HandlerAdapter是负责执行具体处理器的组件。它知道如何调用特定类型的处理器,并返回一个ModelAndView对象,用于渲染视图。
  • DispatcherServlet:DispatcherServlet是Spring MVC的核心组件,充当前端控制器。它接收所有的HTTP请求,协调HandlerMapping、HandlerAdapter和视图解析等组件,最终将请求分发给合适的处理器进行处理。
  • HandlerInterceptor:HandlerInterceptor接口定义了拦截器的基本行为。通过实现该接口,可以在请求处理的不同阶段插入自定义逻辑,如请求前、请求后或完成后的处理。

4. 拦截链的工作流程

了解了核心组件后,我们接下来分析拦截链是如何在这些组件之间协作,主要流程可以总结成下面 7个步骤:

  • 请求到达 DispatcherServlet:所有的HTTP请求首先由DispatcherServlet接收。
  • 查找 Handler:DispatcherServlet使用HandlerMapping查找与请求URL匹配的处理器(Handler)。
  • 应用拦截器前置:在调用处理器之前,DispatcherServlet会调用已注册的所有拦截器的preHandle方法。这些拦截器按照定义的顺序依次执行。如果任意一个拦截器的preHandle返回false,请求将被终止,后续的拦截器和处理器将不会执行。
  • 调用 HandlerAdapter 执行 Handler:所有前置拦截器的preHandle方法返回true后,DispatcherServlet会调用HandlerAdapter执行具体的处理器方法(如Controller中的方法)。
  • 应用拦截器后置:处理器执行完成后,DispatcherServlet会调用拦截器的postHandle方法,这些拦截器按照定义的顺序逆序执行。
  • 渲染视图:DispatcherServlet使用视图解析器(ViewResolver)渲染最终的视图,如返回一个HTML页面。
  • 完成拦截器:最后,DispatcherServlet调用拦截器的afterCompletion方法,通知拦截器请求已经完成,同样按逆序执行。

这个流程确保了拦截器可以在请求处理的不同阶段插入逻辑,例如验证、日志记录、性能监控等。

5. 源码分析

要深入理解拦截链的实现原理,我们需要查看Spring MVC的源码。下面,我们将逐步分析DispatcherServlet、HandlerMapping、HandlerAdapter和HandlerInterceptor等组件的源码,实现对拦截链的全面理解。

(1) DispatcherServlet的角色

DispatcherServlet是整个Spring MVC请求处理流程的中央枢纽。它承担了接收请求、查找处理器、执行拦截器、调用处理器、渲染视图等任务。

让我们看看DispatcherServlet中与拦截链相关的关键源码。

public class DispatcherServlet extends FrameworkServlet {
    // ...其他代码...

    @Override
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // ...初始化上下文...

        // 1. 查找处理器
        HandlerExecutionChain mappedHandler = getHandler(processedRequest);
        if (mappedHandler == null) {
            noHandlerFound(request, response);
            return;
        }

        HttpServletRequest webRequest = createWebRequest(request, response);
        try {
            // 2. 应用拦截器的preHandle方法
            HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
            if (!applyPreHandle(processedRequest, response, mappedHandler, interceptors)) {
                return;
            }

            // 3. 调用处理器
            ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

            // 4. 应用拦截器的postHandle方法
            applyPostHandle(processedRequest, response, mappedHandler, mv, interceptors);

            // 5. 渲染视图
            render(mv, request, response);

            // 6. 应用拦截器的afterCompletion方法
            triggerAfterCompletion(processedRequest, response, mappedHandler, null, interceptors);
        } catch (Exception ex) {
            // 异常处理
            // ...
        }
    }

    // ...其他代码...
}

从上面的代码可以看出,DispatcherServlet在处理请求的过程中,依次执行了查找处理器、应用拦截器的preHandle、调用处理器、应用拦截器的postHandle、渲染视图以及应用拦截器的afterCompletion方法的步骤。

(2) HandlerMapping的查找机制

HandlerMapping用于将请求映射到具体的处理器。Spring MVC提供了多种HandlerMapping实现,如基于注解的RequestMappingHandlerMapping。

来看一个简单的getHandler方法的实现:

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    return this.handlerMappings.getHandler(request);
}

这里,handlerMappings是一个List<HandlerMapping>,Spring会按照顺序遍历这些HandlerMapping,直到找到匹配的处理器。

RequestMappingHandlerMapping的一个核心方法是getHandlerInternal:

@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
    // 基于请求URL和HTTP方法,查找匹配的HandlerMethod
    // 返回一个HandlerMethod对象,包含控制器实例和方法信息
}

(3) HandlerAdapter的适配逻辑

HandlerAdapter负责调用具体的处理器。Spring MVC提供了多种HandlerAdapter实现,如RequestMappingHandlerAdapter。

看看getHandlerAdapter和handle方法:

protected ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    return handlerAdapter.handle(request, response, handler);
}

handlerAdapter是一个HandlerAdapter接口的实现,通过supports方法确定是否适配,然后调用handle方法执行处理器。

RequestMappingHandlerAdapter的handle方法示例:

@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    // 调用Controller方法,处理请求
    // 绑定请求参数,执行数据绑定和验证
    // 返回ModelAndView对象
}

(4) HandlerInterceptor的执行流程

在DispatcherServlet的doDispatch方法中,我们看到了拦截器的调用流程。下面深入看看这些拦截器是如何执行的。

假设我们有一个HandlerExecutionChain对象,它包含了处理器和一组拦截器:

public class HandlerExecutionChain {
    private final Object handler;
    private final List<HandlerInterceptor> interceptors;
    
    // Getters and constructors
}

在applyPreHandle方法中,DispatcherServlet会按照顺序调用每个拦截器的preHandle方法:

protected boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response, HandlerExecutionChain mappedHandler)
        throws Exception {

    HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
    if(!ObjectUtils.isEmpty(interceptors)) {
        for (HandlerInterceptor interceptor : interceptors) {
            if (!interceptor.preHandle(request, response, mappedHandler.getHandler())) {
                triggerAfterCompletion(request, response, mappedHandler, null, interceptors);
                return false;
            }
        }
    }
    return true;
}

类似地,在applyPostHandle和triggerAfterCompletion方法中,拦截器的postHandle和afterCompletion方法依次被调用,且顺序与preHandle相反。

6. 自定义拦截器的实现

了解了拦截链的基础后,我们来看看如何在Spring中自定义拦截器。下面是一个简单的自定义拦截器示例:

public class MyInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 请求处理前执行
        System.out.println("Pre Handle method is Calling");
        returntrue; // 返回true继续流程,返回false则中断
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) throws Exception {
        // 请求处理后但视图渲染前执行
        System.out.println("Post Handle method is Calling");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        // 视图渲染后执行
        System.out.println("Request and Response is completed");
    }
}

注册这个拦截器可以通过Java配置或XML配置。在Spring Boot中,可以通过实现WebMvcConfigurer接口来进行配置:

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyInterceptor())
                .addPathPatterns("/**") // 拦截所有请求
                .excludePathPatterns("/login"); // 排除特定路径
    }
}

通过这样的配置,MyInterceptor就会拦截所有请求,除了/login路径。

(1) 拦截器的执行顺序

如果定义了多个拦截器,Spring会按照注册的顺序执行preHandle方法,而执行postHandle和afterCompletion则是逆序的。例如:

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new FirstInterceptor());
    registry.addInterceptor(new SecondInterceptor());
}

执行顺序:

  • FirstInterceptor.preHandle
  • SecondInterceptor.preHandle
  • SecondInterceptor.postHandle
  • FirstInterceptor.postHandle
  • SecondInterceptor.afterCompletion
  • FirstInterceptor.afterCompletion

这种设计允许后注册的拦截器优先执行后置逻辑和完成逻辑。

7. 总结

本文,我们深入探讨了 Spring拦截链的实现原理和源码分析,从核心组件如DispatcherServlet、HandlerMapping、HandlerAdapter和HandlerInterceptor的功能,到拦截链的工作流程,再到实际的源码分析和自定义拦截器的实现。

理解这些原理,不仅可以帮助我们更好地使用 Spring提供的拦截器功能,还能让我们在需要时自定义复杂的拦截逻辑,增强应用的灵活性和可维护性。

责任编辑:赵宁宁 来源: 猿java
相关推荐

2015-03-31 18:19:37

饿了么动画效果

2017-12-05 15:03:45

人工智能饿了么大数据

2017-07-21 14:48:47

AI物流O2O

2018-01-03 09:57:19

异地双活数据库

2017-10-27 15:44:24

饿了么张龙前端基础设施

2018-08-17 09:14:43

饿了么容器演进

2017-06-12 09:13:02

饿了么技术运营运维

2020-07-06 08:40:36

阿里饿了么思考

2018-11-29 09:36:56

大数据调度系统

2018-02-28 10:45:10

技术变革混合云架构

2016-08-26 22:36:18

大数据

2019-01-04 16:11:50

2016-04-01 18:25:21

中国科技网

2009-01-07 09:23:00

2024-03-28 10:37:44

IoC依赖注入依赖查找

2011-08-30 10:22:14

MongoDB

2021-08-30 11:48:33

开发技术互联网

2015-08-06 17:49:18

容联咻咻验证码

2019-02-26 09:39:46

数据库高可用架构
点赞
收藏

51CTO技术栈公众号