Skip to content

MVC

传统 Java Web 直接基于 Servlet 容器进行开发,HTTP 请求与响应都需要手动进行解析。

Spring MVC 通过注解标识这些信息,对 HTTP 请求参数与 HTTP 响应值进行自动解析。

并且支持拦截器、全局异常处理器等高级功能。

初始化

首先要清楚,前后端接口以方法为单位。

Spring MVC 在最开始,需要解析带有 @RequestMapping 注解的方法信息,随后将这些信息注册进 MappingRegistry 中,供后续参数解析、反射调用等逻辑使用。

相关类

classDiagram
    class RequestMapping{
        <<@interface>>
        String Path
        RequestMethod method
    }
    class HandlerMethod{
        -Object bean
        -Class<?> beanType
        -Method method
        -List~MethodParameter~ parameters
    }
    class RequestMappingInfo{
        -String path
        -RequestMethod httpMethod
    }
    class RequestMethod{
        <<enumeration>>
        GET
        POST
    }
    class MappingRegistry{
        -Map~String, RequestMappingInfo~ pathMappingInfo
        -Map~String, HandlerMethod~ pathHandlerMethod
    }
    class RequestMappingHandlerMapping{
        -MappingRegistry mappingRegistry
        -List~MappedInterceptor~ interceptors
        initialHandlerMethods()
    }
    RequestMappingHandlerMapping o-- RequestMapping
    RequestMappingHandlerMapping *-- MappingRegistry
    MappingRegistry *-- RequestMappingInfo
    MappingRegistry *-- HandlerMethod
    RequestMappingInfo *-- RequestMethod

如何集成进 Spring?

  • RequestMappingHandlerMapping 实现了 InitializingBean 注解,在 Spring 初始化方法中进行 MVC 相关信息的初始化。

关键方法RequestMappingHandlerMapping.initialHandlerMethods

private void initialHandlerMethods() {
    // 1. 获取Spring容器中所有的bean及其beanName
    Map<String, Object> beansOfMap = BeanFactoryUtils.beansOfTypeIncludingAncestors(obtainApplicationContext(), Object.class);
    beansOfMap.entrySet().stream()
            // 2. 过滤出所有带@Controller注解的类
            .filter(entry -> this.isHandler(entry.getValue()))
            // 3. 查找并解析@RequestMapping方法
            // 4. 将URL、请求方式、方法等信息注册进MappingRegistry中,供后续MVC逻辑使用
            .forEach(entry -> this.detectHandlerMethods(entry.getKey(), entry.getValue()));
}

自动解析

参数解析器

相关类

classDiagram
    class RequestBody{
        <<@interface>>
        boolean required
    }
    class RequestParam{
        <<@interface>>
        boolean required
        String name
    }
    class HandlerMethodArgumentResolver{
        <<interface>>
        boolean supportsParameter()
        resolveArgument()
    }
    class HandlerMethodArgumentResolverComposite{
        List~HandlerMethodArgumentResolver~ argumentResolvers
    }
    class RequestBodyMethodArgumentResolver{
    }
    class RequestParamMethodArgumentResolver{
    }
    HandlerMethodArgumentResolver <|-- HandlerMethodArgumentResolverComposite
    HandlerMethodArgumentResolverComposite o-- RequestBody
    HandlerMethodArgumentResolverComposite o-- RequestParam
    HandlerMethodArgumentResolverComposite *-- RequestParamMethodArgumentResolver
    HandlerMethodArgumentResolverComposite *-- RequestBodyMethodArgumentResolver

逻辑

HandlerMethodArgumentResolverComposite 调用每个参数解析器执行。

  • RequestParam 参数解析器:RequestParamMethodArgumentResolver 获取 @RequestParam 注解信息,提取 HttpServletRequest 中的请求行参数,将请求行参数解析为相应的类型。
  • @RequestBody参数解析器: RequestBodyMethodArgumentResolver 获取 @RequestBody 注解信息,提取 HttpServletRequest 中的请求体,将请求体中的 JSON 字符串解析为 Java 对象。

返回值解析器

classDiagram
    class ResponseBody{
        <<@interface>>
    }
    class HandlerMethodReturnValueHandler{
        <<interface>>
        boolean supportsParameter()
        resolveArgument()
    }
    class HandlerMethodReturnValueHandlerComposite{
        List~HandlerMethodReturnValueHandler~ returnValueHandlers
    }
    class ResponseBodyMethodReturnValueHandler{
    }
    HandlerMethodReturnValueHandler <|-- HandlerMethodReturnValueHandlerComposite
    HandlerMethodReturnValueHandlerComposite o-- ResponseBody
    HandlerMethodReturnValueHandlerComposite *-- ResponseBodyMethodReturnValueHandler

逻辑

HandlerMethodReturnValueHandlerComposite 调用每个返回值解析器执行。

  • @ResponseBody 返回值解析器:将带有 @ResponseBody 的对象序列化为 JSON,写入 HttpServletResponse

执行器

classDiagram
    class InvocableHandlerMethod{
        -ParameterNameDiscoverer parameterNameDiscoverer
        -HandlerMethodArgumentResolverComposite argumentResolver
        -HandlerMethodReturnValueHandlerComposite returnValueHandler
        -ConversionService conversionService
        +invokeAndHandle()
    }

自动解析机制的最终执行者

  • 接收 HTTP 请求,通过参数解析器,解析方法参数。
  • 通过反射技术,传入解析好的参数,执行业务方法,获取返回值。
  • 通过返回值解析器,解析返回值,设置 HTTP 响应。

拦截器

相关类

classDiagram
    class HandlerExecutionChain{
        -HandlerMethod handler
        -List~HandlerInterceptor~ interceptors
        +applyPreHandle()
        +applyPostHandle()
        +triggerAfterCompletion()
    }
    class HandlerMethod{
    }
    class HandlerInterceptor{
        <<interface>>
        preHandle()
        postHandle()
        afterCompletion()
    }
    class MappedInterceptor{
        List~String~ includePatterns
        List~String~ excludePatterns
        HandlerInterceptor interceptor
    }
    HandlerExecutionChain *-- HandlerMethod
    HandlerExecutionChain *-- MappedInterceptor
    MappedInterceptor *-- HandlerInterceptor

HandlerExecutionChain 是 Spring MVC 处理业务逻辑的基本单位。

一个 URL 接口可以与多个拦截器匹配,所以构造 HandlerExecutionChain 对象,存储 “接口对应的方法信息” 与 “相匹配的所有拦截器”。

拦截器分为:

  • 前置拦截:一般用于登录校验。
  • 后置处理:能修改方法最终响应的数据。不清楚其应用场景。
  • 最终处理:无论前面的逻辑执行成功还是失败,都会执行这段逻辑。不清楚其应用场景。

DispatcherServlet

sequenceDiagram
    participant C as Client
    participant DS as DispatcherServlet
    participant C1 as 相关类
    participant IHM as InvocableHandlerMethod
    participant C2 as 相关类

alt 初始化
    note over C1: RequestMappingHandlerMapping
    C1->>C1: initialHandlerMethod<br>1. 过滤出所有带@Controller注解的类<br>2. 查找并解析@RequestMapping方法<br>3. 将URL、请求方式、方法等信息注册进<br>MappingRegistry中,供后续MVC逻辑使用
end

C->>DS: HttpServletRequest

    activate DS
    DS->>DS: service(HttpServletRequest, HttpServletResponse)
    DS->>DS: doDispatch
    activate DS
alt 初始化处理器链
    note over C1: HandlerMapping
    DS->>+C1: getHandler
    C1->>C1: createHandlerExecutionChain<br>根据URL识别与该方法匹配的拦截器
    C1-->>-DS: return new HandlerExecutionChain
end
alt 前置拦截
    note over C1: HandlerExecutionChain
    DS->>+C1: applyPreHandle
    C1->>C1: 执行处理器链的前置拦截
    C1-->>-DS: 如果返回 false,退出方法,不执行后续逻辑
end
alt 解析HTTP请求参数,反射执行业务逻辑方法,解析方法返回值写入HTTP响应
    note over C1: HandlerAdapter
    DS->>C1: handle
    activate C1
    C1->>IHM: invokeAndHandle
    deactivate C1
    activate IHM
    note over C2: HandlerMethodArgumentResolverComposite
    IHM->>+C2: resolveArgument
    C2->>C2: 依据@RequestBody、@RequestParam<br>解析方法的每一个参数
    C2-->>-IHM: 返回解析好的参数
    note over C2: Method
    IHM->>+C2: invoke(obj, args)<br>传入参数
    C2->>C2: 反射执行方法
    C2-->>-IHM: 返回方法执行的返回值
    note over C2: HandlerMethodReturnValueHandlerComposite
    IHM->>+C2: handleReturnValue
    deactivate IHM
    C2->>C2: 依据@ReponseBody<br>解析方法返回值
    C2->>-C2: 将返回值写入HTTP响应体中
end
alt 后置处理
    note over C1: HandlerExecutionChain
    DS->>+C1: applyPostHandle
    C1->>-C1: 执行处理器链的后置处理
end

DS-->>C: HttpServletResponse

alt 最终处理
    DS->>+C1: triggerAfterCompletion
    C1->>-C1: 执行处理器链的最终处理
end
    deactivate DS
    deactivate DS

对于拦截器链的执行,最初看可能有些抽象,以下是简化的 DispatcherServlet,帮助理解。

HandlerExecutionChain executionChain = null;
try {
    // 获取拦截器链
    executionChain = handlerMapping.getHandler(request);
    // 前置拦截
    if (!executionChain.applyPreHandle(request, response)) {
        // 如果前置拦截返回 false,直接返回
        return;
    }
    // 执行业务逻辑
    ModelAndView mv = handlerAdapter.handle(request, response, executionChain.getHandler());
    // 后置处理
    executionChain.applyPostHandle(request, response, mv);
} catch (Exception ex) {
} finally {
    if (Objects.nonNull(executionChain)) {
        // 最终处理。无论前面的逻辑是成功还是失败,都会执行
        executionChain.triggerAfterCompletion(request, response, dispatchException);
    }
}

19 岁,正宗双非出身,无学历、无绩点、无竞赛、无论文、无实习,还在爆父母金币。