源码解读全文
SpringMVC请求流程
Tomcat
根据请求路径找到对应的应用,在根据后续的uri
找到对应的servlet
资源HandlerMapping
负责保证url
注册进来,并记录对应的处理方法或Bean
初始化容器完毕后,发布事件,初始化九大组件,初始化过程中,加载配置文件,通过
getBean
创建各大组件,在组件初始化期间调用后置处理器方法,解析Mapping
信息
HandlerAdapter
负责处理这个请求,调用处理方法
Servlet与Service
Servlet
和GenericServlet
只有service
接口HttpServlet
,重写了service
,并拆分成不同的请求方式FrameworkServlet
,重写了doGet/doPost/...
,其中调用processRequest(request, response);
真正处理请求
其中调用了
DispatcherServlet
的doService
方法
- 当一个请求,进行时,调用
HttpServlet
的service
,其中调用FrameworkServlet
中重写的do簇方法
,在调用processRequest(request, response);
时,真正调用了DispatcherServlet
的doService
,最终调用doDispatch(request, response);
doService
DispatcherServlet
调用- 保存
request
域中的对象保存,attributesSnapshot.put(attrName, request.getAttribute(attrName));
,并保存一些内容
容器、国际化解析器、主题解析器
- 初始化闪存管理器,为
重定向
共享数据FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
- 进行请求派发,派发给
controller
,doDispatch(request, response);
,进行真正的资源访问
doDispatch
- 异步请求的支持
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
- 检查是否为文件上传请求
processedRequest = checkMultipart(request);
,处理为新的请求,并更新标志位multipartRequestParsed = (processedRequest != request);
- 获取
handler
执行链,mappedHandler = getHandler(processedRequest);
,决定使用哪个handler
处理当前请求
- 返回的实际上是
HandlerExecutionChain
,目标方法+拦截器- 找不到则返回404
noHandlerFound(processedRequest, response);
- 获取当前方法的适配器,
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
- 调用拦截器方法,进行前置拦截
mappedHandler.applyPreHandle(processedRequest, response)
- 进行对应的处理并获得对应的
mondelAndView
,mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
HttpRequestHandlerAdapter
和SimpleControllerHandlerAdapter
都是调用重写的handlerRequest
RequestMappingHandlerAdapter
处理注解的方法,处理返回值和参数@ResponseBody
在这一步执行执行期间,就已经写出内容
- 没有指定跳转的页面,给一个默认页面
applyDefaultViewName(processedRequest, mv);
通过
RequestToViewNameTranslator
视图翻译器,从request域
中获取默认的,直接返回request
请求的地址,作为默认的view
地址
- 执行拦截器的后置拦截
mappedHandler.applyPostHandle(processedRequest, response, mv);
- 处理结果
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
- 如果有异常,处理异常,返回异常的
ModelAndView
,通过异常解析器
进行解析- 渲染,解析模型和视图
render(mv, request, response);
checkMultipart
- 使用文件上传解析器判断,根据请求头中的
content-type
判断this.multipartResolver.isMultipart(request)
public boolean isMultipart(HttpServletRequest request) {
return StringUtils.startsWithIgnoreCase(request.getContentType(),
(this.strictServletCompliance ? MediaType.MULTIPART_FORM_DATA_VALUE : "multipart/"));
}
getHandler
handlerMapping
保存请求的映射,依次遍历默认的HandlerMapping
,能否处理当前请求
策略模式,是否能处理当前请求
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;
}
BeanNameUrlHandlerMapping
,Bean
的名字作为url
路径进行映射,组件名为/
开始;在初始化完对应的Bean
时,调用初始化前的Aware
后置处理器setApplicationContext
,进行处理,解析BeanName
带斜杠的Handler
,保存在handlerMap
中;HttpRequestHandler
,实现这个接口,并注解为@Controller
,名字以/
开始RequestMappingHandlerMapping
,Request
注解进行映射RouterFunctionHandlerMapping
,支持函数式处理,webflux
相关功能
- 由
RequestMappingHandlerMapping
调用getHandler
,最终调用HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
寻求处理当前请求的handlerMethod
,能执行当前请求的方法
- 通过
MappingRegistry
,找到对应方法,List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
2.lookupPath
,当前访问的路径
- 构造处理器链
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
- 将当前
handler
转换成拦截器,并把环境中所有拦截器加入,从容器中获取- 责任链模式
getHandlerAdapter
- 获取所有的适配器,遍历,看是否支持这个
handler
,也就是执行方法
初始化九大组件中,
加载默认的配置文件
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
//...
}
HttpRequestHandlerAdapter
,处理HttpRequestHandler
接口的处理方法对应BeanNameUrlHandlerMapping
SimpleControllerHandlerAdapter
,处理Controller
接口,对应BeanNameUrlHandlerMapping
RequestMappingHandlerAdapter
,处理HandlerMethod
接口HandlerFunctionAdapter
,处理HandlerFunction
,支持webflux
supports
是否实现了某个handler
接口
public boolean supports(Object handler) {
return (handler instanceof HttpRequestHandler);
}
render
- 视图解析器,通过视图名,解析出视图
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
- 根据不同的
view
渲染视图view.render(mv.getModelInternal(), request, response);
,通过response
返回给前端
RequestMappingHandlerMapping
- 利用
InitializingBean
初始化Mapping
- 包含一个
MappingRegistry
,是一个封装类
registerHandlerMethod
- 解析
Controller
的方法,并进行访问路径的映射,准备映射中心 DispatcherServlet
创建对象后,initServlet()
期间初始化完容器,发布事件,基于事件机制调用initStrategies
,初始化九大组件- 初始化
handlerMapping
,initHandlerMappings(context);
,通过配置文件加载默认的HandlerMapping
,通过IOC容器
创建Bean
,context.getAutowireCapableBeanFactory().createBean(clazz);
- 创建
Bean
后,进行初始化exposedObject = initializeBean(beanName, exposedObject, mbd);
,invokeInitMethods(beanName, wrappedBean, mbd);
其中((InitializingBean) bean).afterPropertiesSet();
RequestMappingHandlerMapping
的父类AbstractHandlerMethodMapping
实现了InitializingBean
的接口,通过重写afterPropertiesSet
进行自定义初始化
- 其中调用
initHandlerMethods();
,保存所有路径和方法的映射信息
initHandlerMethods
- 获取子
web
容器的所有组件getCandidateBeanNames()
,遍历处理 - 分析当前
Bean
的HandlerMethods
,detectHandlerMethods(beanName);
提前进行判断
isHandler(beanType)
,是否有Controller
注解和RequestMapping
注解
getMappingForMethod
中为每一个方法尝试创建请求映射信息RequestMappingInfo info = createRequestMappingInfo(method);
如果不能,则为
null
- 将所有封装后的信息封装成
Map<Method, T> methods
- 遍历每一个方法进行注册,
registerHandlerMethod(handler, invocableMethod, mapping);
- 最终注册到
registry
中,this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directPaths, name, corsConfig != null));
mapping
实际上是路径+请求方式- 会将路径和映射信息保存在
pathLookup
createRequestMappingInfo
- 看当前方法是否有
RequestMapping
注解,如果有,对信息进行封装
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
RequestCondition<?> condition = (element instanceof Class ?
getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}
HandlerAdapter
- 解析请求中的参数,并正确地调用方法,返回
ModelAndView
- 策略模式,动态策略的体现
boolean supports(Object handler);
,决定能否处理这个handler
- 适配器模式,进行处理
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
RequestMappingHandlerAdapter初始化过程
- 利用
InitializingBean
生命周期
RequestMappingHandlerAdapter执行过程
- 反射执行方法,确定参数和返回值
- 调用
handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod)
- 检查请求方式
checkRequest(request);
- 会话锁,每一个用户和服务器交互只有一个会话,保证高并发安全
- 执行目标方法
mav = invokeHandlerMethod(request, response, handlerMethod);
invokeHandlerMethod
- 装饰器模式,封装成
webRequest
,ServletWebRequest webRequest = new ServletWebRequest(request, response);
- 提前准备,准备核心组件
- 数据绑定器,请求数据到参数方法的映射,
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
,数据类型转换、错误处理- 模型工厂,
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
,交给页面的数据
- 封装
HandlerMethod
,ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
,设置参数解析器和返回值处理器,无论目标返回什么,都要适配成ModelAndView
具有执行功能
- 准备模型和视图的临时容器
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
保存处理过程中产生和模型视图有关的数据,在整个请求处理期间共享数据
- 处理异步请求的准备
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
- 做好提前准备后,分为两步
- 执行方法
invocableMethod.invokeAndHandle(webRequest, mavContainer);
- 抽取
ModelAndView
数据,getModelAndView(mavContainer, modelFactory, webRequest);
,数据在model
中,页面在view
中
invokeAndHandle
- 执行目标方法,获取返回值
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
- 获取方法参数列表
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
- 反射执行
doInvoke(args);
- 处理返回值,
this.returnValueHandlers.handleReturnValue( returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
过程中会将数据放在
mavContainer
中
getMethodArgumentValues
- 确定方法参数值
- 获取方法的所有参数以及相关信息
MethodParameter[] parameters = getMethodParameters();
- 遍历确认每个参数的值
Object[] args = new Object[parameters.length];
- 在已提供的参数中找
args[i] = findProvidedArgument(parameter, providedArgs);
实际上是没有的
- 查看能支持处理的参数解析器
this.resolvers.supportsParameter(parameter)
27个参数解析器,负责解析参数
resolveArgument
- 进行参数解析
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
RequestParamMethodArgumentResolver
获取原生request
的参数列表进行匹配- 解析完成后,也会放到
modelAndView
HandlerMethodArgumentResolverComposite
- 策略模式,保存
27
个参数解析器和缓存,负责解析参数 - 遍历所有解析器,是否能够处理该参数
getArgumentResolver(parameter)
- 先从缓存中获取
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
- 遍历解析器,通过类型和注解查看是否支持,
resolver.supportsParameter(parameter)
- 找到之后,放入缓存
- 不同的解析器,有不同的解析方式
resolveArgument
handleReturnValue
- 找到返回值处理器
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
遍历所有返回值处理器,进行判断
- 处理返回值
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
RequestResponseBodyMethodProcessor
优先级比ViewNameMethodReturnValueHandler
高,标注了RequestBody
的String
返回,优先被处理
HandlerMethodReturnValueHandlerComposite
- 策略模式,包括15种返回处理器
handler.supportsReturnType(returnType)
是否中某个类的实现类
RequestResponseBodyMethodProcessor
利用MessageVoncerter
消息转换器,直接获取原生response
的输出流,使用Json
格式输出ViewNameMethodReturnValueHandler
,通过判断返回值是否为redict:
开头,判断是否为重定向
getModelAndView
- 封装成
ModelAndView
- 更新数据,用于请求转发共享数据
modelFactory.updateModel(webRequest, mavContainer);
session
中的数据更新到request
域
- 准备
model
对象ModelMap model = mavContainer.getModel();
- 封装
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
- 重定向数据的共享
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
先把数据移动到
request
,再移动到session
SpringMVC的九大组件
- 全是接口,可以自定义实现
// 文件上传解析器
@Nullable
private MultipartResolver multipartResolver;
// 国际化解析器
/** LocaleResolver used by this servlet. */
@Nullable
private LocaleResolver localeResolver;
// 主题解析器
/** ThemeResolver used by this servlet. */
@Nullable
private ThemeResolver themeResolver;
// handler(处理器,处理请求,也就是Controller)映射,保存所有请求由谁来处理的映射关系
/** List of HandlerMappings used by this servlet. */
@Nullable
private List<HandlerMapping> handlerMappings;
// 处理器的适配器,超级反射工具,将请求反射成目标的处理方法
/** List of HandlerAdapters used by this servlet. */
@Nullable
private List<HandlerAdapter> handlerAdapters;
// 处理器的异常解析器
/** List of HandlerExceptionResolvers used by this servlet. */
@Nullable
private List<HandlerExceptionResolver> handlerExceptionResolvers;
// 把请求转换成视图的翻译器,(页面地址)
/** RequestToViewNameTranslator used by this servlet. */
@Nullable
private RequestToViewNameTranslator viewNameTranslator;
// 闪存管理器,处理重定向数据共享
/** FlashMapManager used by this servlet. */
@Nullable
private FlashMapManager flashMapManager;
// 视图解析器,去哪些页面,怎么过去
/** List of ViewResolvers used by this servlet. */
@Nullable
private List<ViewResolver> viewResolvers;
初始化流程
Tomcat
启动,触发DispatcherServlet
初始化,容器初始化- 发布当前上下文环境刷新完成事件,
publishEvent(new ContextRefreshedEvent(this));
,调用监听器SourceFilteringListener
的onApplicationEvent
利用Spring的事件驱动
- 调用
FrameworkServlet
的onApplicationEvent(event);
,调用onRefresh(event.getApplicationContext());
由子类实现 DispatcherServlet
中重写onRefresh
,调用initStrategies
初始化组件
- 如果需要定制组件,只需要自定义放入组件
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
- 具体初始化流程,都是一样的,先从容器中获取,
- 有就装配,
- 没有就捕获异常,设置为
null
,或根据默认策略进行装配- 只有
MultipartResolver
会为null
,需要导入相关的包,并进行配置
private void initLocaleResolver(ApplicationContext context) {
try {
this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("Detected " + this.localeResolver);
}
else if (logger.isDebugEnabled()) {
logger.debug("Detected " + this.localeResolver.getClass().getSimpleName());
}
}
catch (NoSuchBeanDefinitionException ex) {
// We need to use the default.
// 在`DispatcherServlet类路径下找到资源`DispatcherServlet.properties`,进行资源加载
this.localeResolver = getDefaultStrategy(context, LocaleResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("No LocaleResolver '" + LOCALE_RESOLVER_BEAN_NAME +
"': using default [" + this.localeResolver.getClass().getSimpleName() + "]");
}
}
}
DispatcherServlet.properties
- 读取默认配置文件,进行类加载,并创建对象