很久以前研究过spring mvc,也是看着很多网上的文章,边摸索边学习。如今突然觉得仅仅是看代码学习其实效果不好。经常容易忘记。
学习的最好方式还是自动动手尝试,或者写总结。自从自己弄了个博客后,越来越喜欢写一些文字,算是给自己一个交代。后悔没有早些开始写博客。
好吧废话不多说,仅仅是记录下个人对spring mvc 中 DispatcherServlet的实现逻辑详解。
doService
DispatcherServlet extends FrameworkServlet extends HttpServlet,整个调用链如下图
首先进入的方法是doService,doService接口是在FrameworkServlet中定义的。DispatcherServlet实现,源码如下
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (logger.isDebugEnabled()) {
String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
" processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
}
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// Make framework objects available to handlers and view objects.
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
if (this.flashMapManager != null) {
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
}
try {
doDispatch(request, response);
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}
doService中的代码没啥好说的, 基本上都是在往request 对象中 setAttribute。方便后续方法获取使用对象。mvc整体的架构还是在doDispatch中体现的。
doDispatch
doDispatch体现了mvc的核心架构,在doDispatch中分别包含了如下几点:
- 获取HandlerMapping
- 根据HandlerMapping获取适配的HandlerAdapter
- HandlerAdapter执行controller中的handlerMethod,并返回ModelAndView
- ViewResolver负责解析渲染ModelAndView
DispatcherServlet 核心的架构就是如上过程,当然还有很多细节,比如如何返回json格式的数据,文件上传等等。接下来我们继续深入代码。先看一下doDispatch的核心代码,如下:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
// 显示检查了下是否是文件流请求,如果是文件流,checkMultipart内部会有额外的处理逻辑
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
//A 获取handlerMapping,通过handlerMapping构建HandlerExecutionChain
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// B 根据handlerMapping获取对应的HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
//C 执行HandlerExecutionChain中的前置拦截
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//D 真正去执行controller代码方法的调用入口在这里,当然内部不仅仅是执行controller中的method
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
//E 执行HandlerExecutionChain中的后置处理
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
//
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
以上代码比较重要的基础标识了A、B、C、D,我们来细说下这几处代码。
A 获取handlerMapping,通过handlerMapping构建HandlerExecutionChain
handlerMapping,在spring boot 之前,我们配置DispatcherServlet时都需要配置一个handlerMapping,spring boot就很智能了,不需要我们配了,直接通过注解就实现了默认配置。我们常用的handlerMapping是RequestMappingHandlerMapping和RequestMappingHandlerAdapter。
handlerMapping是做什么的呢,handlerMapping的顶层接口是HandlerMapping,在一个web工程中存在很多不同种类的资源,如静态资源、servlet、通过注解实现的controller、通过实现顶层controller接口实现的controller。每一种不同的资源都对应一个HandlerMapping,通过HandlerMapping在管理这些资源。如下图
在HandlerMapping中管理着所有的mapping映射资源,如下图
上图中我们看到HandlerMapping中使用一个Map来存储mapping映射资源的,想象一下一个request请求如何将请求转发到controller中的method。靠的就是HandlerMapping,调用链如下:
- HandlerMapping.getHandler
- AbstractHandlerMapping.getHandlerInternal
- AbstractHandlerMapping.getHandlerExecutionChain
在DispatcherServlet.doDispatch中A段代码进入到getHandler(),通过上面的介绍HandlerMapping是多个的,所以循环HandlerMapping集合,调用handlerMapping.getHandler()方法,如果返回不为空,那么就直接return了,代码如下
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
// 执行getHandler方法,返回的是HandlerExecutionChain,下面会介绍内容逻辑
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
HandlerExecutionChain 包含了HandlerInterceptor(拦截器),HandlerMethod(需要执行的controller中的method)。到这里是不是就很清晰了。接下来我们来看看mvc是如何构建HandlerExecutionChain 对象的。
从 AbstractHandlerMapping.getHandlerInternal开始说起,代码如下
@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 获取HandlerMethod
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
// 构建HandlerExecutionChain
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
- getHandlerInternal核心代码在AbstractHandlerMethodMapping,逻辑是根据request的URI获取HandlerMapping中的HandlerMethod
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
...
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
...
}
- getHandlerExecutionChain核心代码就是加入拦截器,HandlerInterceptor相信大家应该不陌生,不知道有没有人用这个做简单的日志记录的。
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
// MappedInterceptor 可以匹配URL进行拦截
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
// 其他拦截器,所有HandlerMehtod都会拦截
chain.addInterceptor(interceptor);
}
}
return chain;
}
到了目前为止,HandlerMapping和HandlerExecutionChain 应该就很清楚了,HandlerMapping找到request请求唯一的HandlerMethod,并且封装成HandlerExecutionChain,而HandlerExecutionChain中就包含了HandlerMethod和HandlerInterceptor。接下来就是HandlerAdapter也就是DispatcherServlet.doDispatch中B段代码
获取HandlerAdapter
获取HandlerAdapter逻辑很简单,截两张图,handler是HandlerExecutionChain.getHandler()得到的对象通过参数传递过来的,我们知道此时的handler对象就是HandlerMethod。所以得到的HandlerAdapter 就是RequestMappingHandlerAdapter
但是我们依然要知道为什么需要HandlerAdapter,我们上面说过HandlerMapping是有很多类型的,比如servlet、静态资源、注解controller、接口controller。现象一下,每一种类型其实处理的方式都不一样,比如参数注入、结果解析等等每一种HandlerMapping都可能不一样,比如Servlet和注解Controller在参数注入和结果解析明显就有很大的不同之处, 想想我们的设计模式,单一原则等等,很自然就需要多一层HandlerAdapter。而HandlerAdapter就是真正要去调用Method的处理类。
获取到HandlerAdapter就好办了,调用下前置处理(代码片段C),接下来的事情就全部交给HandlerAdapter吧(代码片段D)
调用HandlerAdapter.handler接口
HandlerAdapter.handler调用链如下
-
AbstractHandlerMethodAdapter.handler
-
RequestMappingHandlerAdapter.handleInternal
-
RequestMappingHandlerAdapter.invokeHandlerMethod
-
ServletInvocableHandlerMethod.invokeAndHandle
核心代码在RequestMappingHandlerAdapter.invokeHandlerMethod和ServletInvocableHandlerMethod.invokeAndHandle -
RequestMappingHandlerAdapter.invokeHandlerMethod代码如下
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
if (logger.isDebugEnabled()) {
logger.debug("Found concurrent result value [" + result + "]");
}
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
上面代码,看似复杂其实就是为了构建ServletInvocableHandlerMethod,ServletInvocableHandlerMethod几个重要的对象是WebDataBinderFactory和ModelFactory 看这些命名基本上就知道是为了参数绑定注入、Model构建(最后返回我们的ModuleAndView中的Model)。
- ServletInvocableHandlerMethod.invokeAndHandle代码如下
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 参数解析绑定,并利用反射调用Method
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
// 处理返回结果(比如返回json)
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
}
throw ex;
}
}
上面代码主要目的就是参数解析绑定然后反射调用函数,函数调用返回值的处理。invokeForRequest核心调用链
- invokeForRequest
- getMethodArgumentValues
getMethodArgumentValues中最核心的代码其实argumentResolvers,
private Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
MethodParameter[] parameters = getMethodParameters();
Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
args[i] = resolveProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
// 参数解析核心代码入口
if (this.argumentResolvers.supportsParameter(parameter)) {
try {
args[i] = this.argumentResolvers.resolveArgument(
parameter, mavContainer, request, this.dataBinderFactory);
continue;
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug(getArgumentResolutionErrorMessage("Failed to resolve", i), ex);
}
throw ex;
}
}
if (args[i] == null) {
throw new IllegalStateException("Could not resolve method parameter at index " +
parameter.getParameterIndex() + " in " + parameter.getExecutable().toGenericString() +
": " + getArgumentResolutionErrorMessage("No suitable resolver for", i));
}
}
return args;
}
一直玩下追踪,HandlerMethodArgumentResolverComposite
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
//获取方法参数解析器
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
if (resolver == null) {
throw new IllegalArgumentException("Unknown parameter type [" + parameter.getParameterType().getName() + "]");
}
//解析参数
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
//获取方法参数解析器的方法
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
if (result == null) {
for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) {
if (logger.isTraceEnabled()) {
logger.trace("Testing if argument resolver [" + methodArgumentResolver + "] supports [" +
parameter.getGenericParameterType() + "]");
}
if (methodArgumentResolver.supportsParameter(parameter)) {
result = methodArgumentResolver;
this.argumentResolverCache.put(parameter, result);
break;
}
}
}
return result;
}
HandlerMethodArgumentResolver是参数解析器,只有两个方法,但是有很多的实现方案
实现方案
-
RequestParamMethodArgumentResolver
支持两种场景。一种是含@RequestParam注解的参数,另一种就是简单类型,如Integer、String、Date、URI, URL,Locale等 -
RequestParamMapMethodArgumentResolver
用来获取所有的header信息。参数类型可以是普通的Map类型,也可以是MultiValueMap或者进一步的Http请求参数,他们与普通Map类型的区别是他们对value值后者们是以List形式存放,前者是以String形式存放。 -
RequestHeaderMethodArgumentResolver
主要用来处理含有@RequestHeader注解的参数,但同时该参数又不是Map类型。 -
RequestHeaderMapMethodArgumentResolver
用来获取所有的header信息。参数类型可以是普通的Map类型,也可以是MultiValueMap或者进一步的HttpHeaders,他们与普通Map类型的区别是他们对value值后者们是以List形式存放,前者是以String形式存放。 -
ServletCookieValueMethodArgumentResolver
主要处理处理器方法参数中带有@CookieValue的解析工作。它的验证代码在AbstractCookieValueMethodArgumentResolver中 -
PathVariableMethodArgumentResolver
主要针对含有@PathVariable的参数。必须要指定@PathVariable的值,即这个ArgumentResolver只能获取一个uri变量,要想获取多个则要使用PathVariableMapMethodArgumentResolver。 -
PathVariableMapMethodArgumentResolver
它要求必须含有@PathVariable注解,并且必须是Map类型,并且@PathVariable注解的value没有值。
解析并绑定参数后,就可以利用java反射原理实现函数调用了。函数执行完成后,处理返回值,回到ServletInvocableHandlerMethod.invokeAndHandle代码,继续往下处理返回值的代码在类HandlerMethodReturnValueHandlerComposite中
public void handleReturnValue(Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
//获取返回值处理器
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if (handler == null) {
throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
}
//处理返回值
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
//获取返回值处理器
private HandlerMethodReturnValueHandler selectHandler(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;
}
因为现在大部分后端处理都是直接返回的json数据,所以这里就直接输出json数据,并将ModelAndViewContainer对象中的requestHandler属性置为true。这样在RequestMappingHandlerAdapter上层代码中就不会在返回ModuleAndView对象了。
public void handleReturnValue(Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
//这里设置了请求已被处理,在上层方法中将不会返回ModelAndView对象了。
mavContainer.setRequestHandled(true);
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
// Try even with null return value. ResponseBodyAdvice could get involved.
//将找到返回值转换
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
// 以下代码在RequestMappingHandlerAdapter中
@Nullable
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
modelFactory.updateModel(webRequest, mavContainer);
// ResponseBody注解,ModelAndView 为空
if (mavContainer.isRequestHandled()) {
return null;
}
ModelMap model = mavContainer.getModel();
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
if (request != null) {
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
}
return mav;
}
好了,本次先写ViewandResolve了。时间有限,感觉篇幅太长,很多东西也无法细讲