源码解读-springmvc源码探秘

此去经年,夏梦未央。我以为你会是我最温暖的夏阳。只是我忘了,我们都无力阻止四季的轮转

Posted by yishuifengxiao on 2021-02-07

一 基础详解

DispatcherServlet是HTTP请求处理程序/控制器的中央调度程序,例如 适用于Web UI控制器或基于HTTP的远程服务导出器。 向注册的处理程序调度以处理Web请求,从而提供便利的映射和异常处理功能。

该Servlet非常灵活:安装适当的适配器类后,几乎可以用于任何工作流程。 它提供以下功能,使其区别于其他请求驱动的Web MVC框架:

  • 它基于JavaBeans配置机制。
  • 它可以使用任何HandlerMapping实现(预先构建或作为应用程序的一部分提供)来控制将请求路由到处理程序对象。 默认值为org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping和org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping。 可以在Servlet的应用程序上下文中将HandlerMapping对象定义为bean,实现HandlerMapping接口,并覆盖默认的HandlerMapping(如果存在)。 可以给HandlerMappings任何bean名称(它们通过类型进行测试)。
  • 可以使用任何HandlerAdapter; 这允许使用任何处理程序接口。 默认适配器是org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,用于Spring的org.springframework.web.HttpRequestHandler和org.springframework.web.servlet.mvc.Controller接口, 分别。 默认的org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter也将被注册。 可以将HandlerAdapter对象作为Bean添加到应用程序上下文中,从而覆盖默认的HandlerAdapters。 像HandlerMappings一样,可以为HandlerAdapters提供任何bean名称(它们通过类型进行测试)。
  • 可以通过HandlerExceptionResolver指定调度程序的异常解决策略,例如,将某些异常映射到错误页面。 默认值为org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver和org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver。 可以通过应用程序上下文覆盖这些HandlerExceptionResolvers。 可以给HandlerExceptionResolver任何bean名称(它们通过类型进行测试)。
  • 可以通过ViewResolver实现来指定其视图解析策略,将符号视图名称解析为View对象。 默认值为org.springframework.web.servlet.view.InternalResourceViewResolver。 可以将ViewResolver对象作为bean添加到应用程序上下文中,从而覆盖默认的ViewResolver。 可以给ViewResolvers任何bean名称(它们通过类型进行测试)。
  • 如果用户未提供视图或视图名称,则配置的RequestToViewNameTranslator将当前请求转换为视图名称。 对应的bean名称是“ viewNameTranslator”; 默认值为org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator。
  • 调度程序解决多部分请求的策略由MultipartResolver实现确定。 其中包括对Apache Commons FileUpload和Servlet 3的实现。 典型的选择是org.springframework.web.multipart.commons.CommonsMultipartResolver。 MultipartResolver bean的名称是“ multipartResolver”; 默认为无。
  • 它的语言环境解析策略由LocaleResolver确定。 现成的实现通过HTTP accept标头,cookie或会话来工作。 LocaleResolver Bean名称为“ localeResolver”; 默认值为org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver。
  • 它的主题解析策略由ThemeResolver确定。 包括用于固定主题以及cookie和会话存储的实现。 ThemeResolver Bean名称为“ themeResolver”。 默认值为org.springframework.web.servlet.theme.FixedThemeResolver。

只有在调度程序中存在相应的HandlerMapping(用于类型级注释)或HandlerAdapter(用于方法级注释)时,才会处理@RequestMapping注释。 默认情况下就是这种情况。 但是,如果要定义自定义HandlerMappings或HandlerAdapters,则需要确保也定义了相应的自定义RequestMappingHandlerMapping和/或RequestMappingHandlerAdapter,前提是您打算使用@RequestMapping。

Web应用程序可以定义任意数量的DispatcherServlet。 每个Servlet将在其自己的名称空间中运行,并使用映射,处理程序等加载其自己的应用程序上下文。仅共享由org.springframework.web.context.ContextLoaderListener加载的根应用程序上下文(如果有)。

从Spring 3.1开始,现在可以向DispatcherServlet注入Web应用程序上下文,而不是在内部创建它自己的上下文。 这在Servlet 3.0+环境中非常有用,该环境支持以编程方式注册Servlet实例。

image-20210206142722411

二 初始化

在springboot中,DispatcherServlet是在org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration 中初始化的,其初始化代码如下

@Configuration(proxyBeanMethods = false)
@Conditional(DefaultDispatcherServletCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
protected static class DispatcherServletConfiguration {

@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());
return dispatcherServlet;
}

@Bean
@ConditionalOnBean(MultipartResolver.class)
@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
public MultipartResolver multipartResolver(MultipartResolver resolver) {
// Detect if the user has created a MultipartResolver but named it incorrectly
return resolver;
}

}

image-20210206150455270

image-20210206150534822

三 运行流程

image-20210207091642758

3.1 基本流程

关键部分代码如下

/**
* Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
* for the actual dispatching.
*/
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
logRequest(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);
}
}
}
}

private void logRequest(HttpServletRequest request) {
LogFormatUtils.traceDebug(logger, traceOn -> {
String params;
if (isEnableLoggingRequestDetails()) {
params = request.getParameterMap().entrySet().stream()
.map(entry -> entry.getKey() + ":" + Arrays.toString(entry.getValue()))
.collect(Collectors.joining(", "));
}
else {
params = (request.getParameterMap().isEmpty() ? "" : "masked");
}

String queryString = request.getQueryString();
String queryClause = (StringUtils.hasLength(queryString) ? "?" + queryString : "");
String dispatchType = (!request.getDispatcherType().equals(DispatcherType.REQUEST) ?
"\"" + request.getDispatcherType().name() + "\" dispatch for " : "");
String message = (dispatchType + request.getMethod() + " \"" + getRequestUri(request) +
queryClause + "\", parameters={" + params + "}");

if (traceOn) {
List<String> values = Collections.list(request.getHeaderNames());
String headers = values.size() > 0 ? "masked" : "";
if (isEnableLoggingRequestDetails()) {
headers = values.stream().map(name -> name + ":" + Collections.list(request.getHeaders(name)))
.collect(Collectors.joining(", "));
}
return message + ", headers={" + headers + "} in DispatcherServlet '" + getServletName() + "'";
}
else {
return message;
}
});
}

/**
* Process the actual dispatching to the handler.
* <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
* The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
* to find the first that supports the handler class.
* <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
* themselves to decide which methods are acceptable.
* @param request current HTTP request
* @param response current HTTP response
* @throws Exception in case of any kind of processing failure
*/
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 {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);

// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}

// Determine handler adapter for the current request.
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 (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}

if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}

// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

if (asyncManager.isConcurrentHandlingStarted()) {
return;
}

applyDefaultViewName(processedRequest, mv);
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);
}
}
}
}

【注意】 这些方法都是被 protected 修饰,而不是public

其主要工作流程如下

  1. 首先检查是否为文件上传,如果是文件上传,就结束此流程
  2. 根据请求获取对应的Handler(其实是HandlerExecutionChain实例)
  3. 根据handler获取对应的HandlerAdapter
  4. 应用注册拦截器的preHandle方法
  5. 执行此HandlerAdapter,得到对应的ModelAndView(使用给定的处理程序来处理此请求。 所需的工作流程可能相差很大)
  6. 应用此ModelAndView,判断改视图是否需要翻译
  7. 应用注册拦截器的postHandle方法。
  8. 处理处理程序选择和处理程序调用的结果,该结果可以是ModelAndView或要解析为ModelAndView的异常。

3.2 获取Handler

在上面提到过,DispatcherServlet是根据请求获取对应的Handler的,该部分代码如下

/**
* Return the HandlerExecutionChain for this request.
* <p>Tries all handler mappings in order.
* @param request current HTTP request
* @return the HandlerExecutionChain, or {@code null} if no handler could be found
*/
@Nullable
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的过程中,系统会尝试匹配每一个已经组件的handler。在springboot项目中,系统默认已经注册了7个Handler实例,他们分别为

image-20210206161335263

在上面过程中使用到的Handler的结构如下

image-20210206162322987

在匹配Handler时,会首先调用AbstractHandlerMapping的getHandler()方法,该方法的具体代码如下

image-20210206165037273

在上面函数中涉及到getHandlerInternal是一个抽象方法,具体的实现需要看其实例是怎么实现的,以SimpleUrlHandlerMapping为例,其实现代码如下

@Override
@Nullable
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
request.setAttribute(LOOKUP_PATH, lookupPath);
Object handler = lookupHandler(lookupPath, request);
if (handler == null) {
// We need to care for the default handler directly, since we need to
// expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
Object rawHandler = null;
if (StringUtils.matchesCharacter(lookupPath, '/')) {
rawHandler = getRootHandler();
}
if (rawHandler == null) {
rawHandler = getDefaultHandler();
}
if (rawHandler != null) {
// Bean name or resolved handler?
if (rawHandler instanceof String) {
String handlerName = (String) rawHandler;
rawHandler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(rawHandler, request);
handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
}
}
return handler;
}

注意该代码其实是在SimpleUrlHandlerMapping的基类AbstractUrlHandlerMapping中的

经过上面的查找过程后,得到对应的处理实例

image-20210206170457235

与此同时,在获取到对应的 handler 实例后,系统会尝试获取此handler对应的HandlerExecutionChain。该部分代码的主要作用为给定处理程序(包括适用的拦截器)构建HandlerExecutionChain。
默认实现使用给定的处理程序,处理程序映射的公共侦听器以及与当前请求URL匹配的任何MappedInterceptors来构建标准的HandlerExecutionChain。 拦截器按照注册时的顺序添加。 子类可以重写此方法,以扩展/重新排列拦截器的列表。

注意:传入的处理程序对象可以是原始处理程序或预建的HandlerExecutionChain。 此方法应显式处理这两种情况,要么构建新的HandlerExecutionChain,要么扩展现有链。

为了简单地在自定义子类中添加拦截器,请考虑调用super.getHandlerExecutionChain(handler,request)并在返回的链对象上调用HandlerExecutionChain.addInterceptor。

image-20210206165930790

经过上述一系列查找流程之后,系统会获取到与之匹配的RequestMappingHandlerMapping实例。

【注意】如果系统设置了跨域配置,这里就能看到作用

image-20210206165131511

3.3 获取HandlerAdapter

与查找handler一样,查找HandlerAdapter的过程其实也是遍历系统中所有注册的HandlerAdapter实例,然后获取到对应的HandlerAdapter,只不过是handler根据请求来探查的,而HandlerAdapter则是根据上一步找到handler来查找的。具体的代码如下

/**
* Return the HandlerAdapter for this handler object.
* @param handler the handler object to find an adapter for
* @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.
*/
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");
}

系统中注册好的HandlerAdapter如下

image-20210206171252096

在查找时,主要是判断当前HandlerAdapter是否支持传入的handler,匹配的代码如下

/**
* This implementation expects the handler to be an {@link HandlerMethod}.
* @param handler the handler instance to check
* @return whether or not this adapter can adapt the given handler
*/
@Override
public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}

注意该代码是在抽象类AbstractHandlerMethodAdapter中,以系统中默认匹配到的RequestMappingHandlerAdapter为例,具体的构造如下:

image-20210206171756868

在真正执行HandlerAdapter时,流程会先进入抽象类AbstractHandlerMethodAdapter的handle方法中

/**
* This implementation expects the handler to be an {@link HandlerMethod}.
*/
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {

return handleInternal(request, response, (HandlerMethod) handler);
}

然后由器具体的实现类来做真正的实现。例如在RequestMappingHandlerAdapter中的实现过程如下

@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

ModelAndView mav;
checkRequest(request);

// Execute invokeHandlerMethod in synchronized block if required.
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No HttpSession available -> no mutex necessary
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No synchronization on session demanded at all...
//在微服务框架中,流程会直接进去这里
mav = invokeHandlerMethod(request, response, handlerMethod);
}

if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}

return mav;
}

经过一些跳转之后,接下来流程会进入org.springframework.web.method.HandlerMethod.doInvoke()方法,由此进入真正的流程。

/**
* Invoke the handler method with the given argument values.
*/
@Nullable
protected Object doInvoke(Object... args) throws Exception {
ReflectionUtils.makeAccessible(getBridgedMethod());
try {
//这里进入真正的流程
return getBridgedMethod().invoke(getBean(), args);
}
catch (IllegalArgumentException ex) {
assertTargetBean(getBridgedMethod(), getBean(), args);
String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
throw new IllegalStateException(formatInvokeError(text, args), ex);
}
catch (InvocationTargetException ex) {
// Unwrap for HandlerExceptionResolvers ...
Throwable targetException = ex.getTargetException();
if (targetException instanceof RuntimeException) {
throw (RuntimeException) targetException;
}
else if (targetException instanceof Error) {
throw (Error) targetException;
}
else if (targetException instanceof Exception) {
throw (Exception) targetException;
}
else {
throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);
}
}
}

3.4 数据输出

这一步实在流程的最后一步,此部分主要负责处理处理程序选择和处理程序调用的结果,该结果可以是ModelAndView或要解析为ModelAndView的异常。代码如下

/**
* Handle the result of handler selection and handler invocation, which is
* either a ModelAndView or an Exception to be resolved to a ModelAndView.
*/
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {

boolean errorView = false;

if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}

// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("No view rendering, null ModelAndView returned.");
}
}

if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}

if (mappedHandler != null) {
// Exception (if any) is already handled..
mappedHandler.triggerAfterCompletion(request, response, null);
}
}

在这里,一般会很迷惑,对于RESTFUL请求,这里返回的mv的数据为null,那么我们执行的结果的数据时如何输出的呢,代码最终的奥秘是在这里 org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle()

/**
* Invoke the method and handle the return value through one of the
* configured {@link HandlerMethodReturnValueHandler HandlerMethodReturnValueHandlers}.
* @param webRequest the current request
* @param mavContainer the ModelAndViewContainer for this request
* @param providedArgs "given" arguments matched by type (not resolved)
*/
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {

Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);

if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
disableContentCachingIfNecessary(webRequest);
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 {
//这里返回数据
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}

具体的处理过程如下
org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite

image-20210207114749296

继续执行,流程会进入到

org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters,在经过一系列HttpMessageConverter后将数据输出,真正使用到的实例为org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor

/**
* Writes the given return type to the given output message.
* @param value the value to write to the output message
* @param returnType the type of the value
* @param inputMessage the input messages. Used to inspect the {@code Accept} header.
* @param outputMessage the output message to write to
* @throws IOException thrown in case of I/O errors
* @throws HttpMediaTypeNotAcceptableException thrown when the conditions indicated
* by the {@code Accept} header on the request cannot be met by the message converters
* @throws HttpMessageNotWritableException thrown if a given message cannot
* be written by a converter, or if the content-type chosen by the server
* has no compatible converter.
*/
@SuppressWarnings({"rawtypes", "unchecked"})
protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

Object body;
Class<?> valueType;
Type targetType;

if (value instanceof CharSequence) {
body = value.toString();
valueType = String.class;
targetType = String.class;
}
else {
body = value;
valueType = getReturnValueType(body, returnType);
targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());
}

if (isResourceType(value, returnType)) {
outputMessage.getHeaders().set(HttpHeaders.ACCEPT_RANGES, "bytes");
if (value != null && inputMessage.getHeaders().getFirst(HttpHeaders.RANGE) != null &&
outputMessage.getServletResponse().getStatus() == 200) {
Resource resource = (Resource) value;
try {
List<HttpRange> httpRanges = inputMessage.getHeaders().getRange();
outputMessage.getServletResponse().setStatus(HttpStatus.PARTIAL_CONTENT.value());
body = HttpRange.toResourceRegions(httpRanges, resource);
valueType = body.getClass();
targetType = RESOURCE_REGION_LIST_TYPE;
}
catch (IllegalArgumentException ex) {
outputMessage.getHeaders().set(HttpHeaders.CONTENT_RANGE, "bytes */" + resource.contentLength());
outputMessage.getServletResponse().setStatus(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE.value());
}
}
}

MediaType selectedMediaType = null;
MediaType contentType = outputMessage.getHeaders().getContentType();
boolean isContentTypePreset = contentType != null && contentType.isConcrete();
if (isContentTypePreset) {
if (logger.isDebugEnabled()) {
logger.debug("Found 'Content-Type:" + contentType + "' in response");
}
selectedMediaType = contentType;
}
else {
HttpServletRequest request = inputMessage.getServletRequest();
List<MediaType> acceptableTypes = getAcceptableMediaTypes(request);
List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);

if (body != null && producibleTypes.isEmpty()) {
throw new HttpMessageNotWritableException(
"No converter found for return value of type: " + valueType);
}
List<MediaType> mediaTypesToUse = new ArrayList<>();
for (MediaType requestedType : acceptableTypes) {
for (MediaType producibleType : producibleTypes) {
if (requestedType.isCompatibleWith(producibleType)) {
mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));
}
}
}
if (mediaTypesToUse.isEmpty()) {
if (body != null) {
throw new HttpMediaTypeNotAcceptableException(producibleTypes);
}
if (logger.isDebugEnabled()) {
logger.debug("No match for " + acceptableTypes + ", supported: " + producibleTypes);
}
return;
}

MediaType.sortBySpecificityAndQuality(mediaTypesToUse);

for (MediaType mediaType : mediaTypesToUse) {
if (mediaType.isConcrete()) {
selectedMediaType = mediaType;
break;
}
else if (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) {
selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
break;
}
}

if (logger.isDebugEnabled()) {
logger.debug("Using '" + selectedMediaType + "', given " +
acceptableTypes + " and supported " + producibleTypes);
}
}

if (selectedMediaType != null) {
selectedMediaType = selectedMediaType.removeQualityValue();
for (HttpMessageConverter<?> converter : this.messageConverters) {
GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
(GenericHttpMessageConverter<?>) converter : null);
if (genericConverter != null ?
((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
converter.canWrite(valueType, selectedMediaType)) {
//这里得到的是最终输出的数据
body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
(Class<? extends HttpMessageConverter<?>>) converter.getClass(),
inputMessage, outputMessage);
if (body != null) {
Object theBody = body;
LogFormatUtils.traceDebug(logger, traceOn ->
"Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]");
addContentDispositionHeader(inputMessage, outputMessage);
if (genericConverter != null) {
//输出数据
genericConverter.write(body, targetType, selectedMediaType, outputMessage);
}
else {
//输出数据
((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Nothing to write: null body");
}
}
return;
}
}
}

if (body != null) {
Set<MediaType> producibleMediaTypes =
(Set<MediaType>) inputMessage.getServletRequest()
.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);

if (isContentTypePreset || !CollectionUtils.isEmpty(producibleMediaTypes)) {
throw new HttpMessageNotWritableException(
"No converter for [" + valueType + "] with preset Content-Type '" + contentType + "'");
}
throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
}
}