源码解读全文
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都是调用重写的handlerRequestRequestMappingHandlerAdapter处理注解的方法,处理返回值和参数@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接口的处理方法对应BeanNameUrlHandlerMappingSimpleControllerHandlerAdapter,处理Controller接口,对应BeanNameUrlHandlerMappingRequestMappingHandlerAdapter,处理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
- 读取默认配置文件,进行类加载,并创建对象