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 岁,正宗双非出身,无学历、无绩点、无竞赛、无论文、无实习,还在爆父母金币。