引言
作为一名Java开发实习生,今天分享一下自己的知识学习。Spring框架已经成为我们开发工作中不可或缺的一部分。虽然Spring框架带来了极大的便利,但它有时也会让我们忽视一些底层的技术细节,不了解对编写代码貌似影响不大,但解决bug时,如果百度到了还好,百度不到只能一头雾水,两眼干瞪。在Spring Web应用中,一个很简单的问题:1个http请求通过网络到达我们的服务器,是怎么执行java代码的?
在学习SpringMVC之前,先认识一下Servlet。
一、Servlet
什么是servlet?servlet运行在哪?
可以看见servlet就是一个接口;接口就是规定了一些规范,使得一些具有某些共性的类都能实现这个接口,从而都遵循某些规范。有的人往往以为就是servlet直接处理客户端的http请求,其实并不是这样,servlet并不会去监听8080端口;直接与客户端打交道是“容器”,比如常用的tomcat。
二、SpringMVC整体流程与十大常用组件
任何Spring Web的entry point(入口点,即应用程序开始处理HTTP请求的地方),都是servlet。
先了解一下整体流程和SpringMVC常用的10个组件
1、DispatcherServlet:前端控制器
springmvc 处理请求的入口,作用:统一处理请求和响应,整个流程控制的中心,由它来调用其他组件处理用户的请求。
2、HandlerMapping:处理器映射器
getHandler:根据请求查找请求处理器,会返回一个 HandlerExecutionChain
对象。
public interface HandlerMapping {
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
3、HandlerExecutionChain:处理器执行链
请求处理器,通常就是我们自定义的 controller 对象及方法
public class HandlerExecutionChain {
// 请求处理器,通常就是我们自定义的 controller 对象及方法
private final Object handler;
// 拦截器,当前请求匹配到的拦截器列表
private final List<HandlerInterceptor> interceptorList = new ArrayList<>();
// 拦截器索引,用来记录执行到第几个拦截器了
private int interceptorIndex = -1;
}
4、handler:处理器
一般指我们自定义的 controller,在 DispatcherServlet
的控制下 handler 对具体的请求进行处理。
5、HandlerAdapter:处理器适配器
它负责对 handler 的方法进行调用,由于 handler 的类型可能有很多种,每种 handler 的调用过程可能不一样,此时就需要用到适配器 HandlerAdapte
public interface HandlerAdapter {
// 当前 HandlerAdapter 是否支持 handler
boolean supports(Object handler);
// 其内部负责调用 handler 的来处理用户的请求,返回返回一个 ModelAndView 对象
@Nullable
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}
6、ModelAndView:模型和视图
public class ModelAndView {
// 视图
@Nullable
private Object view;
// 模型,用来存放共享给客户端的数据
@Nullable
private ModelMap model;
}
7、ViewResolver:视图解析器
它负责视图解析,根据视图的名称得到对应的视图对象(View)
public interface ViewResolver {
// 根据视图的名称得到对应的视图对象
@Nullable
View resolveViewName(String viewName, Locale locale) throws Exception;
}
8、View:视图
render 方法根据指定的模型数据(model)渲染视图,即 render 方法负责将结果输出给客户端。
public interface View {
void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception;
}
9、HandlerExceptionResolver:处理器异常解析器
负责处理异常的,HandlerExceptionResolver
接口有个resolveException
方法,用来解析异常,返回异常情况下对应的 ModelAndView 对象
public interface HandlerExceptionResolver {
@Nullable
ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
}
10、HttpMessageConverter:http 报文转换器
将请求报文转换为 Java 对象,或将 Java 对象转换为响应报文,在处理@RequestBody、RequestEntity、@ResponseBody、ResponseEntity 的时候会用到
public interface HttpMessageConverter<T> {
// 是否可以将请求报文读取给方法参数指定的类型
boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);
// 是否可以将响应的报文转换为方法参数指定的类型输出
boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);
// 当前转换器支持的类型
List<MediaType> getSupportedMediaTypes();
// 当前转换器支持的类型
default List<MediaType> getSupportedMediaTypes(Class<?> clazz) {
return (canRead(clazz, null) || canWrite(clazz, null) ?
getSupportedMediaTypes() : Collections.emptyList());
}
// 将http报文转换为给定的类型,然后返回
T read(Class<? extends T> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException;
// 将给定的对象t,转换为http报文输出到客户端
void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException;
}
三、SpringMVC处理流程详解
springmvc 的所有请求,最终都会到org.springframework.web.servlet.DispatcherServlet#doDispatch
这个方法
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 请求对象
HttpServletRequest processedRequest = request;
// 处理器执行链对象
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
// 获取异步处理管理器,servlet3.0后支持异步处理,可以在子线程中响应用户请求
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
//模型和视图
ModelAndView mv = null;
//异常对象
Exception dispatchException = null;
try {
// 1.解析multipart类型的请求,上传文件用的就是multipart类型的请求方式
processedRequest = checkMultipart(request);
// 用来标记是否是multipart类型的请求
multipartRequestParsed = (processedRequest != request);
// 2.根据请求获取HandlerExecutionChain对象
mappedHandler = getHandler(processedRequest);
//如果没有找到处理器,就404了
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 3.根据处理器获取HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 4.调用拦截器的preHandle方法,若返回false,处理结束
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 5.调用handler实际处理请求,获取ModelAndView对象,这里会调用HandlerAdapter#handle方法处理请求,其内部会调用handler来处理具体的请求
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 判断异步请求不是已经开始了,开始了就返回了
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 如果mv对象中没有视图 & DispatcherServlet配置了默认的视图,则给mv安排一个默认的视图
applyDefaultViewName(processedRequest, mv);
// 6.调用拦截器的postHandle方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception ex) {
dispatchException = ex;
} catch (Throwable err) {
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
// 7.处理分发结果,渲染视图(包含了正常处理和异常情况的处理),将结果输出到客户端
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
} catch (Exception ex) {
// 8.调用拦截器的afterCompletion方法
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
} catch (Throwable err) {
// 8.调用拦截器的afterCompletion方法
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
} finally {
// 对于异步处理的情况,调用异步处理的拦截器AsyncHandlerInterceptor的afterConcurrentHandlingStarted方法
if (asyncManager.isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
} else {
// 对于multipart的请求,清理资源,比如文件上传的请求,在上传的过程中文件会被保存到临时文件中,这里就会对这些文件继续清理
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
1、解析 multipart 类型的请求
上传文件用的就是multipart类型的请求方式
protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
// 判断multipartResolver解析器是否存在 && 请求是否是multipart类型
if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
// 将请求转换为multipart类型的请求对象,通常为MultipartHttpServletRequest类型
return this.multipartResolver.resolveMultipart(request);
}
return request;
}
2、根据请求获取 HandlerExecutionChain 对象
HandlerExecutionChain包含handler、interceptorList、interceptorIndex3个信息
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
handler 类型为HandlerMethod
public class HandlerMethod {
protected final Log logger = LogFactory.getLog(this.getClass());
// 通常是我们自定义的controller对象
private final Object bean;
@Nullable
private final BeanFactory beanFactory;
private final Class<?> beanType;
// 能够处理当前请求的方法
private final Method method;
private final Method bridgedMethod;
private final MethodParameter[] parameters;
@Nullable
private HttpStatus responseStatus;
@Nullable
private String responseStatusReason;
@Nullable
private HandlerMethod resolvedFromHandlerMethod;
@Nullable
private volatile List<Annotation[][]> interfaceParameterAnnotations;
private final String description;
3、根据处理器获取 HandlerAdapter
遍历HandlerAdapter列表,找到能够处理当前 handler 的HandlerAdapter,如果没找到会报错
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
4、调用拦截器的 preHandle 方法
// 调用拦截器的preHandle方法,若返回false,处理结束
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
主要干了 3 个事情
- 循环调用拦截器的preHandle方法
- 如果某个拦截器的preHandle方法返回 false,则反向依次调用那些 preHandle 方法返回 ture 的拦截器的 afterCompletion 方法;这句话有点绕,比如有 3 个拦截器,1、2 的 preHandler 返回了 true,而 3 返回的是 false,那么这里将按照 2、1 的顺序调用他们的 afterCompletion 方法
- 记录拦截器的执行位置
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
for (int i = 0; i < this.interceptorList.size(); i++) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
// 调用拦截器的preHandle方法
if (!interceptor.preHandle(request, response, this.handler)) {
// 如果拦截器返回false,则反向依次调用那些preHandle方法返回ture的拦截器的afterCompletion方法
triggerAfterCompletion(request, response, null);
return false;
}
// 记录当前拦截器执行的位置
this.interceptorIndex = i;
}
return true;
}
triggerAfterCompletion
方法
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
try {
interceptor.afterCompletion(request, response, this.handler, ex);
} catch (Throwable ex2) {
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}
通过拦截器当前执行的位置interceptorIndex
逆向调用拦截器的afterCompletion
方法
5、调用 handler 实际处理请求,获取 ModelAndView 对象
// 调用handler实际处理请求,获取ModelAndView对象,
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
ha.handle 方法内部过程会走到RequestMappingHandlerAdapter#invokeHandlerMethod
方法,这个方法内部会通过反射调用@RequestMapping 标注的方法,这个方法主要做了 3 个重要的事情:
- step1:组装目标方法需要的参数
public interface HandlerMethodArgumentResolver {
// 判断当前解析器是否能处理这个parameter这个参数
boolean supportsParameter(MethodParameter parameter);
// 解析参数:从http请求中解析出控制器需要的参数的值
Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
}
这个接口有很多实现类,列几个常见的
实现类 | 对应的控制器参数 | 说明 |
---|---|---|
PathVariableMapMethodArgumentResolver | @PathVariable 标注参数 | 从 url 中提取参数的值 |
RequestParamMethodArgumentResolver | @RequestParam 标注参数 | 从http 请求参数中获取值 |
RequestResponseBodyMethodProcessor | @RequestBody 标注参数 | 提取 body 数据,转换为参数类型 |
… | … | … |
- step2:通过反射调用处理请求的目标方法,获取方法的返回值
也就是调用 controller 中的@RequestMapping 标注的方法org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle
调用地方:调用处理请求的实际方法的时候+方法有异常的时候,异常解析器里面
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
// 通过反射调用目标方法,内部会组装目标方法需要的参数
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
// 如果返回值为空,表示目标方法中已经完成了请求的所有处理,表示请求处理结束了,将执行mavContainer.setRequestHandled(true)标记请求处理完毕
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true);
return;
}
} // 若getResponseStatusReason()不为空,表示请求已经处理过了
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
// 走到这里,说明有返回值,标记请求未处理完毕
mavContainer.setRequestHandled(false);
// 对返回值进行处理
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
- step3:对方法的返回值进行处理
- 会对上述反射调用的结果 returnValue 进行处理,
org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite#handleReturnValue
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
// 根据返回值找到HandlerMethodReturnValueHandler
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if (handler == null) {
throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
}
// 调用HandlerMethodReturnValueHandler#handleReturnValue处理返回值
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
@Nullable
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
// 根据返回值判断是否是异步请求
boolean isAsyncValue = isAsyncReturnValue(value, returnType);
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
continue;
}
if (handler.supportsReturnType(returnType)) {
return handler;
}
}
return null;
}
HandlerMethodReturnValueHandler
这个接口用来处理返回值
public interface HandlerMethodReturnValueHandler {
// 是否能够处理 returnType 参数指定的返回值
boolean supportsReturnType(MethodParameter returnType);
// 处理返回值
void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;
}
此接口有很多实现类,列举几个
实现类 | 说明 |
---|---|
ViewNameMethodReturnValueHandler | 返回值为视图名称时的解析器 |
RequestResponseBodyMethodProcessor | 方法上标注有@ResponseBody 注解时返回值的解析器 |
MapMethodProcessor | 返回值为 Map 的解析器 |
StreamingResponseBodyReturnValueHandler | 返回值为 ResponseEntity 类型时的解析器 |
… | … |
6、调用拦截器的 postHandle 方法
// 调用拦截器的postHandle方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
逆序调用拦截器的postHandle方法
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {
for (int i = this.interceptorList.size() - 1; i >= 0; i--) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
interceptor.postHandle(request, response, this.handler, mv);
}
}
7、渲染视图
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
// 7处理分发结果,渲染视图(包含了正常处理和异常情况的处理),将结果输出到客户端
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
// 如果有异常,进行全局异常处理
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
if (mv != null && !mv.wasCleared()) {
// 渲染视图
render(mv, request, response);
if (errorView) {
//调用request.removeAttribute方法清理request中错误信息
WebUtils.clearErrorRequestAttributes(request);
}
}
if (mappedHandler != null) {
// 调用拦截器的afterCompletion方法
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
这个方法主要干了 3 个事情
- step1:如果有异常,进行全局异常处理
if (exception != null) {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
// 7-1如果有异常,进行全局异常处理
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
org.springframework.web.servlet.DispatcherServlet # processHandlerException
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
@Nullable Object handler, Exception ex) throws Exception {
// 调用处理器异常解析器解析异常,得到ModelAndView
ModelAndView exMv = null;
if (this.handlerExceptionResolvers != null) {
for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
exMv = resolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
}
if (exMv != null) { // 暴露异常信息到request对象中(request.setAttribute)
WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
return exMv;
}
throw ex;
}
HandlerExceptionResolver 接口:处理器异常解析器,内部就只有一个方法,用来解析异常的,得到一个 ModelAndView 对象。
public interface HandlerExceptionResolver {
@Nullable
ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
}
- step2:渲染视图
render(mv, request, response);
// 7-2 渲染视图
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
View view;
String viewName = mv.getViewName();
if (viewName != null) {
// 调用视图解析器解析视图名称得到视图View对象
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
} else {
view = mv.getView();
}
// 调用视图的render方法渲染视图,将结果输出到客户端
view.render(mv.getModelInternal(), request, response);
}
此方法干了 2 件事
- 调用视图解析器解析视图名称得到视图 View 对象
- 调用视图的 render 方法渲染视图,将结果输出到客户端
// 调用视图解析器解析视图名称得到视图 View 对象
protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
Locale locale, HttpServletRequest request) throws Exception {
if (this.viewResolvers != null) {
for (ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
}
return null;
}
// 调用视图的 render 方法渲染视图,将结果输出到客户端
view.render(mv.getModelInternal(), request, response);
- step3:调用拦截器的 afterCompletion 方法
mappedHandler.triggerAfterCompletion(request, response, null);
// 反向调用拦截器的afterCompletion方法
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
try {
interceptor.afterCompletion(request, response, this.handler, ex);
} catch (Throwable ex2) {
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}
四、扩展:过滤器和拦截器的区别是什么
在Tomcat中,一次请求会先进入到Tomcat容器,然后经过Filter的处理,处理通过之后才会进入到Servlet容器,进入到Servlet容器之后,才会在Servlet执行的前后执行Intercepter。
维度不同:过滤器是基于Java Servlet规范实现的,可以通过配置web.xml文件进行实现。拦截器是基于Spring框架实现的,可以通过定义拦截器类实现。拦截器可以对请求进行更加精细的控制,例如进行AOP、权限控制、事务管理等操作。
定义一个过滤器
需要实现javax.servlet.Filter接口
public class LoginFilter implements Filter {
public void init(FilterConfig config) throws ServletException { // 初始化 }
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
// 登录验证和处理
chain.doFilter(req, resp);
}
public void destroy() { // 销毁 }
}
并且需要在web.xml文件中把他配置上:
<filter>
<filter-name>LoginFilter</filter-name>
<filter-class>com.jd.LoginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>LoginFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
定义一个拦截器
需要实现org.springframework.web.servlet.HandlerInterceptor接口:
@Component
public class LoginInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 登录验证和处理
return true;
}
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// 后处理
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 完成处理
}
}
并且在SpringMVC中进行配置:
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/api/*"/>
<bean class="com.jd.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
或者
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/api/*");
}
}
五、扩展:SpringMVC是如何将不同的Request路由到不同Controller中的?
根据上面的学习,可以将问题转化为:不同的request如何拿到对应的HandlerMethod
SpringMvc启动会把带有@RequestMapping
注解的方法和类封装成一个RequestMappingInfo
和HandlerMethod
,然后注册到MappingRegistry
。当HttpServletRequest
访问时,会通过AbstractHandlerMethodMapping#lookupHandlerMethod
方法获取对应的HandlerMethod
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
// 1.先通过url获取到对应的RequestMappingInfo集合
List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
if (directPathMatches != null) {
// 2.把RequestMappingInfo和HandlerMethod放到match里面
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
}
if (!matches.isEmpty()) {
Match bestMatch = matches.get(0);
// 3.如果匹配到多个Match(譬如url相同但是方法不同),则通过RequestMappingInfo中的各种condition匹配出对应的bestMatch
if (matches.size() > 1) {
}
// 4.获取match中的HandlerMethod
return bestMatch.getHandlerMethod();
} else {
return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
}
}
假设我们有两个 HandlerMethod:
- 第一个HandlerMethod使用了
@RequestMapping("/users/{id}")
。 - 第二个HandlerMethod使用了
@RequestMapping("/users/123")
。
对于请求 /users/123
,Spring MVC 会比较这两个 HandlerMethod 的 RequestMappingInfo
中的 PathPatternsRequestCondition
条件,以确定哪一个是最匹配的。
- 第一个HandlerMethod的
PathPatternsRequestCondition
与请求/users/123
的匹配度较低,因为它需要从路径中解析{id}
。 - 第二个HandlerMethod的
PathPatternsRequestCondition
与请求/users/123
的匹配度较高,因为它是直接匹配的。
一个http请求中,携带有不同的信息,如url,method,header等等,SpringMVC通过Match类统一封装所有的RequestMappingInfo中的各种condition,同时利用compare方法,直接比较出最优的那个handlerMethod。
六、总结
深入阅读代码有助于我们全面了解代码的执行流程,从而在开发过程中做到心中有数。这样的习惯不仅能够帮助我们在编写代码时更加自信,还能让我们在遇到bug时迅速定位问题,从容应对。因此,深入研究代码不仅是提升个人技能的有效途径,也是确保项目顺利进行的重要保障。
如有错误,还请指正,谢谢大家!
感谢大家的观看!!!创作不易,如果觉得我写的好的话麻烦点点赞👍支持一下,谢谢!!!
相关文章已在掘金发布,体验更佳!!