一次POST调用,响应结果乱码问题排查(2)

7 篇文章 0 订阅
2 篇文章 0 订阅

参考1:Http中Content-Type与Accept的区别
参考2:SpringBoot2.3.9 乱码问题分析解决
参考3:SpringMVC

以下只是对 SpringBoot 2.0.6.RELEASE 版本进行分析

1. SpringMVC 原理简述

SpringMVC执行流程图
在这里插入图片描述
SpringMVC执行流程:

  • 1、用户发送请求到前端控制器DispatcherServlet
  • 2、DispatcherServlet收到请求调用HandlerMapping处理器映射器
  • 3、处理器映射器找到具体的处理器(注解或者xml配置),生成处理器对象以及处理器拦截器,返回给DispatcherServlet
  • 4、DispatcherServlet调用HandlerAdapter处理器适配器
  • 5、HandlerAdapter经过适配器调用具体的处理器(Controller)
  • 6、Controller执行完成后返回ModelAndView
  • 7、HandlerAdapter将Controller执行结果ModelAndView返回给DispatcherServlet
  • 8、DispatcherServlet将ModelAndView传给ViewRslover视图解析器
  • 9、视图解析器解析后返回具体的View
  • 10、DispatcherServlet根据View进行渲染视图(将模型数据填充到视图,也就是前端的页面jsp等)
  • 11、DispatcherServlet响应用户。

名词解释:

  • DispatcherServlet前端控制器:是整个流程的控制中心,相当于中央处理器,接收请求,响应结果,降低各个组件的耦合度,提高组件的扩展性。
  • HandlerMapping处理器映射器:根据url来寻找Handler,可以配置文件方式,注解等。
  • HandlerAdapter处理器适配器:按照规则去执行Handler
  • Handler处理器:这个就是我们写的东西,就是业务需求。
  • View Resolver视图解析器:根据逻辑视图名解析成物理视图名,即具体的页面地址,再生成view视图对象,最后对view进行渲染将处理结果通过页面展示给用户
  • View视图:比如jsp等。

2. 初始化 url 与 RequestMappingInfo 的映射关系

2.1 调用流程

url 与 RequestMappingInfo(接口方法相关信息) 的映射关系,是在服务启动时开始的,详细代码就不在这里展示了,罗列下调用流程:

  • Application.main(),也就是服务的启动入口
  • SpringApplication#run(java.lang.String…)
  • WebMvcAutoConfiguration.EnableWebMvcConfiguration#requestMappingHandlerMapping
  • WebMvcConfigurationSupport#requestMappingHandlerMapping
  • WebMvcConfigurationSupport#createRequestMappingHandlerMapping
  • RequestMappingHandlerMapping#afterPropertiesSet
  • AbstractHandlerMethodMapping#afterPropertiesSet
  • AbstractHandlerMethodMapping#initHandlerMethods
    • RequestMappingHandlerMapping#isHandler
  • AbstractHandlerMethodMapping#detectHandlerMethods
    • RequestMappingHandlerMapping#getMappingForMethod
    • RequestMappingHandlerMapping#createRequestMappingInfo
    • RequestMappingHandlerMapping#createRequestMappingInfo
  • AbstractHandlerMethodMapping#registerHandlerMethod
  • AbstractHandlerMethodMapping.MappingRegistry#register
  • .AbstractHandlerMethodMapping.MappingRegistry#urlLookup urlLookup 是 一个内部属性,类型为MultiValueMap<String, T>,就是该属性储存的 url 与 controller 信息的关系映射

出现下图中的日志,就是表明已经将 url 与 controller 的映射关系已配置好。
在这里插入图片描述

2.2 跟进 AbstractHandlerMethodMapping#initHandlerMethods

该方法就是将 url 与 RequestMappingInfo(接口方法相关信息) 关系存储到AbstractHandlerMethodMapping.MappingRegistry#urlLookup 字段中,该字段的类型为 MultiValueMap<String, T>,key 为url,value 为 RequestMappingInfo。如下图

在这里插入图片描述

相关源码
初始化 Handler(Controller)的相关方法。先获取所有 beanName 的数组(String[]),再遍历 beanName 数组,根据 beanName 获取 beanType(也就是 beanName 所对应的 Class 类),再判断 beanType 是不是 Handler(校验类上有没有 Controller、RequestMapping 注解),是的话就执行 detectHandlerMethods(),不是就进行下一轮。

protected void initHandlerMethods() {
	if (logger.isDebugEnabled()) {
		logger.debug("Looking for request mappings in application context: " + getApplicationContext());
	}

	// 获取beanName数组,应该是容器里面的所有bean吧,具体没有研究,瞎猜的
	String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
			BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
			obtainApplicationContext().getBeanNamesForType(Object.class));
	
	// 遍历上面获取到的 beanName 数组
	for (String beanName : beanNames) {
		if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
			// 根据 beanName 获取其对应 Class 类
			Class<?> beanType = null;
			try {
				beanType = obtainApplicationContext().getType(beanName);
			}
			catch (Throwable ex) {
				// An unresolvable bean type, probably from a lazy bean - let's ignore it.
				if (logger.isDebugEnabled()) {
					logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
				}
			}
			
			// 判断 beanType 是否为空,以及是否是一个 Handler
			if (beanType != null && isHandler(beanType)) {
				// 根据方法名直译,是检测 Hander 的所有方法
				detectHandlerMethods(beanName);
			}
		}
	}
	handlerMethodsInitialized(getHandlerMethods());
}

该方法就是判断 bean 是不是 Handler,也就是看类上有没有 Controller、RequestMapping 注解,有的话就返回 true(是Handler),没有就返回 false(不是Handler)

protected boolean isHandler(Class<?> beanType) {
	return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
			AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
protected void detectHandlerMethods(Object handler) {
	
	// 根据 beanName 获取其对应 Class 类,也就是我们写的 Controller 类
	Class<?> handlerType = (handler instanceof String ?
			obtainApplicationContext().getType((String) handler) : handler.getClass());

	if (handlerType != null) {
		
		// 从该 Handler 获取方法上有 @PostMapping 等注解的方法,并存在 methods 中,没有的就不存了
		
		// getUserClass 方法就是查看类名称上是不是包含 $$ 字符,包含就获取超类,不包含就原封不动的返回,这里的 userType 和 handlerType 一摸一样
		Class<?> userType = ClassUtils.getUserClass(handlerType);

		// 此处大概功能为,从 handlerType (也就是我们写的 Controller 类)中,获取所有的方法,让后再去校验该方法上是否包含 RequestMapping 注解,不包含则返回 null, 包含则返回一个 RequestMappingInfo 对象,并将符合的方法存在 methods 中。methods 的 key 为 Controller 中的方法信息, value 为 RequestMappingInfo 对象信息
		// RequestMappingInfo 存储的是 接口路径、接口请求方式(GET、POST。。。)等信息
		Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
				(MethodIntrospector.MetadataLookup<T>) method -> {
					try {	
						
						// 通过 method 对象和 handlerType (Controller)获取方法所对应的 RequestMappingInfo 信息
						return getMappingForMethod(method, userType);
					}
					catch (Throwable ex) {
						throw new IllegalStateException("Invalid mapping on handler class [" +
								userType.getName() + "]: " + method, ex);
					}
				});
		
		// 打印 Debug 日志,日志类似于这样
		// Mapped "{[/bill/api/v1/queryCallDetail2],methods=[POST]}" onto public com.aispeech.smart.bill.model.RestResp<java.util.List<com.aispeech.smart.bill.model.CallDetail>> com.aispeech.smart.bill.controller.CallDetailController.queryCallDetail2(java.lang.String)		
		if (logger.isDebugEnabled()) {
			logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
		}
		methods.forEach((method, mapping) -> {
			// 可以看成就是 method 
			Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
			// 注册上面获取到的所有 HandlerMethod,其实就是将 url 与 HandlerMethod 做了一个绑定,并将绑定关系存在了一个 Map 中
			registerHandlerMethod(handler, invocableMethod, mapping);
		});
	}
}

getUserClass 方法在 ClassUtils 类中,主要作用就是查看类名称上是不是包含 $$ 字符,包含就获取超类,不包含就原封不动的返回

public static final String CGLIB_CLASS_SEPARATOR = "$$";

public static Class<?> getUserClass(Class<?> clazz) {
	if (clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) {
		Class<?> superclass = clazz.getSuperclass();
		if (superclass != null && superclass != Object.class) {
			return superclass;
		}
	}
	return clazz;
}

以下三个方法,主要是通过 method 对象和 handlerType (Controller)获取方法所对应的 RequestMappingInfo 信息

protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
	RequestMappingInfo info = createRequestMappingInfo(method);
	if (info != null) {
		RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
		if (typeInfo != null) {
			info = typeInfo.combine(info);
		}
	}
	return info;
}

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);
}

protected RequestMappingInfo createRequestMappingInfo(
		RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {

	RequestMappingInfo.Builder builder = RequestMappingInfo
			.paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
			.methods(requestMapping.method())
			.params(requestMapping.params())
			.headers(requestMapping.headers())
			.consumes(requestMapping.consumes())
			.produces(requestMapping.produces())
			.mappingName(requestMapping.name());
	if (customCondition != null) {
		builder.customCondition(customCondition);
	}
	return builder.options(this.config).build();
}

注册获取到的所有 HandlerMethod,其实就是将 url 与 HandlerMethod 做了一个绑定,并将绑定关系存在了一个 Map 中

protected void registerHandlerMethod(Object handler, Method method, T mapping) {
	this.mappingRegistry.register(mapping, handler, method);
}

public void register(T mapping, Object handler, Method method) {
	this.readWriteLock.writeLock().lock();
	try {
		// 通话handler(类的beanName名称)、method,获取 HandlerMethod
		HandlerMethod handlerMethod = createHandlerMethod(handler, method);
		assertUniqueMethodMapping(handlerMethod, mapping);
		this.mappingLookup.put(mapping, handlerMethod);

		if (logger.isInfoEnabled()) {
			logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod);
		}
		
		// 获取mapping (RequestMappingInfo)中、获取接口 url 列表
		List<String> directUrls = getDirectUrls(mapping);
		for (String url : directUrls) {
			// 将 url 与 RequestMappingInfo 的关系存起来,分析到这里也就可以了,这里就将我们所写的 controller 中的接口,存储了起来,以供后面访问该接口时,通过请求的 url 找到对应的 接口方法(RequestMappingInfo )。
			this.urlLookup.add(url, mapping);
		}

		String name = null;
		if (getNamingStrategy() != null) {
			name = getNamingStrategy().getName(handlerMethod, mapping);
			addMappingName(name, handlerMethod);
		}

		CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
		if (corsConfig != null) {
			this.corsLookup.put(handlerMethod, corsConfig);
		}

		this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
	}
	finally {
		this.readWriteLock.writeLock().unlock();
	}
}

3. SpringMVC 执行流程源码

3.1 DispatcherServlet(前端控制器)

DispatcherServlet是整个SpringMVC框架的入口,充当一个前端控制器,所有的请求会先到DispatcherServlet然后转发到具体的Controller进行处理,故DispatcherServlet是学习SpringMVC框架的入口。(类结构图,如下)
在这里插入图片描述
从类结构图可以看出,DispatcherServlet最终继承自Servlet,或者说它就是一个Servlet,当然从类名也可以很明确的知道。既然是Servlet,那DispatcherServlet的生命周期必然也是调用init()方法进行初始化、doService()进行具体的请求处理、destroy()进行销毁。

接下来我们省略掉 init()、destroy() 方法,直接来看下 doService() 方法的源码

3.1.1 doService()

每次发起请求时,都会调用 Servlet.service() 方法,因为DispatcherServlet 作为 Servlet 子类,因此最终会调用 DispatcherServlet.doService() 方法。

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));
			}
		}
	}

	// 使框架对象可用于处理程序和视图对象。将一些框架信息设置到 request 的属性中,用于后续处理 handler 和 view。
	// 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 {
		// 一个Web请求的所有处理流程全在这个方法里面
		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);
			}
		}
	}
}

3.1.2 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 {
			processedRequest = checkMultipart(request);
			multipartRequestParsed = (processedRequest != request);

			// Determine handler for the current request.
			// 根据请求的 url 来获取对应的 HandlerExecutionChain,这个方法就是根据 url 在初始化时存储的 url 与 RequestMappingInfo 的 Map 中获取到 RequestMappingInfo,然后再将其封装成一个执行链,返回给 DispatcherServlet。
			mappedHandler = getHandler(processedRequest);
			if (mappedHandler == null) {
				noHandlerFound(processedRequest, response);
				return;
			}

			// Determine handler adapter for the current request.
			// 根据 HandlerExecutionChain 获取对应的 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;
				}
			}

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

			// Actually invoke the handler.
			// HandlerAdapter 再去处理具体的 Handler,并返回对应的 MAV 对象。
			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);
			}
		}
	}
}

从 HandlerMapping 列表中,查找与 请求的 url 匹配的 HandlerMethod,并将 HandlerMethod 封装成一个 HandlerExecutionChain 返回给 DispatcherServlet

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() + "'");
			}
			HandlerExecutionChain handler = hm.getHandler(request);
			if (handler != null) {
				return handler;
			}
		}
	}
	return null;
}

获取 request 的 HandlerExecutionChain

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
	// 根据 request 获取对应的方法的 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);
	}
	
	// 将获取到的 HandlerMethod 封装成一的 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;
}

根据 request 获取对应的方法的 HandlerMethod
AbstractHandlerMethodMapping#getHandlerInternal

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
	
	// 获取请求的 url
	String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
	if (logger.isDebugEnabled()) {
		logger.debug("Looking up handler method for path " + lookupPath);
	}
	this.mappingRegistry.acquireReadLock();
	try {
		// 根据请求的 url 获取对应的方法的 HandlerMethod 
		HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
		if (logger.isDebugEnabled()) {
			if (handlerMethod != null) {
				logger.debug("Returning handler method [" + handlerMethod + "]");
			}
			else {
				logger.debug("Did not find handler method for [" + lookupPath + "]");
			}
		}
		return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
	}
	finally {
		this.mappingRegistry.releaseReadLock();
	}
}

根据请求的 url 获取对应的方法的 HandlerMethod

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
	List<Match> matches = new ArrayList<>();
	List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
	if (directPathMatches != null) {
		addMatchingMappings(directPathMatches, matches, request);
	}
	if (matches.isEmpty()) {
		// No choice but to go through all mappings...
		addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
	}

	if (!matches.isEmpty()) {
		Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
		matches.sort(comparator);
		if (logger.isTraceEnabled()) {
			logger.trace("Found " + matches.size() + " matching mapping(s) for [" + lookupPath + "] : " + matches);
		}
		Match bestMatch = matches.get(0);
		if (matches.size() > 1) {
			if (CorsUtils.isPreFlightRequest(request)) {
				return PREFLIGHT_AMBIGUOUS_MATCH;
			}
			Match secondBestMatch = matches.get(1);
			if (comparator.compare(bestMatch, secondBestMatch) == 0) {
				Method m1 = bestMatch.handlerMethod.getMethod();
				Method m2 = secondBestMatch.handlerMethod.getMethod();
				throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +
						request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");
			}
		}
		handleMatch(bestMatch.mapping, lookupPath, request);
		return bestMatch.handlerMethod;
	}
	else {
		return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
	}
}

根据请求的 url 获取对应的 RequestMappingInfo 信息,也就是对应的接口方法。urlLookup 里面的信息是在服务启动时,存储的。前面章节(2. 初始化 url 与 RequestMappingInfo 的映射关系)已做过分析

public List<T> getMappingsByUrl(String urlPath) {
	return this.urlLookup.get(urlPath);
}

从 handlerAdapters(处理器适配器列表) 中判断 handler 是属于那种适配器的,并将匹配上的适配器返回

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
		for (HandlerAdapter ha : this.handlerAdapters) {
			if (logger.isTraceEnabled()) {
				logger.trace("Testing handler adapter [" + ha + "]");
			}
			// 校验 handler 的类型是否为 HandlerMethod 
			if (ha.supports(handler)) {
				return ha;
			}
		}
	}
	throw new ServletException("No adapter for handler [" + handler +
			"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}

校验 handler 的类型是否为 HandlerMethod
AbstractHandlerMethodAdapter#supports

public final boolean supports(Object handler) {
	return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}

protected boolean supportsInternal(HandlerMethod handlerMethod) {
	return true;
}
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handle
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
		throws Exception {

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

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInternal
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.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod
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();
	}
}

org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
		Object... providedArgs) throws Exception {
	
	// 解析请求参数,并调用 Controller 中接口方法,获取响应结果
	Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
	
	// 设置请求结果(Json字符串),响应状态
	setResponseStatus(webRequest);
	
	// 校验响应结果是否为null
	if (returnValue == null) {
		if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
			mavContainer.setRequestHandled(true);
			return;
		}
	}	

	// 校验 ResponseStatusReason 是否为空
	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(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
		}
		throw ex;
	}
}

org.springframework.web.method.support.InvocableHandlerMethod#invokeForRequest

public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
		Object... providedArgs) throws Exception {
	
	// 获取请求参数
	Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
	if (logger.isTraceEnabled()) {
		logger.trace("Invoking '" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
				"' with arguments " + Arrays.toString(args));
	}
	// 这里就会调用我们自己写的 Controller 类中的接口
	Object returnValue = doInvoke(args);
	if (logger.isTraceEnabled()) {
		logger.trace("Method [" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
				"] returned [" + returnValue + "]");
	}
	return returnValue;
}

这里就会调用我们自己写的 Controller 类中的接口
org.springframework.web.method.support.InvocableHandlerMethod#doInvoke

protected Object doInvoke(Object... args) throws Exception {
	ReflectionUtils.makeAccessible(getBridgedMethod());
	try {
		// 这里就会调用我们自己写的 Controller 类中的接口
		return getBridgedMethod().invoke(getBean(), args);
	}
	catch (IllegalArgumentException ex) {
		assertTargetBean(getBridgedMethod(), getBean(), args);
		String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
		throw new IllegalStateException(getInvocationErrorMessage(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 {
			String text = getInvocationErrorMessage("Failed to invoke handler method", args);
			throw new IllegalStateException(text, targetException);
		}
	}
}

我们自己写的 Controller

@RequestMapping("/bill/api/v1")
@Slf4j
@RestController
public class CallDetailController {

    @Autowired
    private CallDetailService callDetailService;

    @PostMapping("/queryCallDetail")
    public RestResp<List<CallDetail>> queryCallDetail(@RequestBody String param) {
        log.info("query call detail param is :{}", param);

        long startTime = System.currentTimeMillis();
        RestResp<List<CallDetail>> resp = new RestResp<List<CallDetail>>();
        try {
            resp = callDetailService.queryBySessionId(param);
        } catch (Exception e) {
            resp.setCode(500);
            log.error("queryCallDetail error :{},{}", param, e);
        }
        long endTime = System.currentTimeMillis();
        log.info("query call detail spend :{}ms", (endTime - startTime));
        return resp;
    }

    @PostMapping("/queryCallDetail2")
    public RestResp<List<CallDetail>> queryCallDetail2(@RequestBody String param) {
        log.info("query call detail param is :{}", param);

        long startTime = System.currentTimeMillis();
        RestResp<List<CallDetail>> resp = new RestResp<List<CallDetail>>();
        try {
            resp = callDetailService.queryBySessionId(param);
        } catch (Exception e) {
            resp.setCode(500);
            log.error("queryCallDetail error :{},{}", param, e);
        }
        long endTime = System.currentTimeMillis();
        log.info("query call detail spend :{}ms", (endTime - startTime));
        return resp;
    }

    public RestResp<List<CallDetail>> queryCallDetail3(String param) {
        log.info("query call detail param is :{}", param);

        long startTime = System.currentTimeMillis();
        RestResp<List<CallDetail>> resp = new RestResp<List<CallDetail>>();
        try {
            resp = callDetailService.queryBySessionId(param);
        } catch (Exception e) {
            resp.setCode(500);
            log.error("queryCallDetail error :{},{}", param, e);
        }
        long endTime = System.currentTimeMillis();
        log.info("query call detail spend :{}ms", (endTime - startTime));
        return resp;
    }
}

设置请求结果(Json字符串),响应状态

org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#setResponseStatus

private void setResponseStatus(ServletWebRequest webRequest) throws IOException {
	// 获取到的 status == nll,直接返回
	HttpStatus status = getResponseStatus();
	if (status == null) {
		return;
	}

	HttpServletResponse response = webRequest.getResponse();
	if (response != null) {
		String reason = getResponseStatusReason();
		if (StringUtils.hasText(reason)) {
			response.sendError(status.value(), reason);
		}
		else {
			response.setStatus(status.value());
		}
	}

	// To be picked up by RedirectView
	webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, status);
}

org.springframework.web.method.HandlerMethod#getResponseStatus

protected HttpStatus getResponseStatus() {
	// 默认值为 null
	return this.responseStatus;
}
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
		ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
	
	// 选择一个支持 returnType 的 handler
	HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
	if (handler == null) {
		throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
	}
	
	// 使用选中的 handler 进行返回值解析处理
	handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
		throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

	mavContainer.setRequestHandled(true);
	ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
	ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

	// Try even with null return value. ResponseBodyAdvice could get involved.
	// 将结果写入到 response 中
	writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
			ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
			throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
	Object outputValue;
	Class<?> valueType;
	Type declaredType;

	if (value instanceof CharSequence) {
		outputValue = value.toString();
		valueType = String.class;
		declaredType = String.class;
	}
	else {
		outputValue = value;
		valueType = getReturnValueType(outputValue, returnType);
		declaredType = getGenericType(returnType);
	}

	if (isResourceType(value, returnType)) {
		outputMessage.getHeaders().set(HttpHeaders.ACCEPT_RANGES, "bytes");
		if (value != null && inputMessage.getHeaders().getFirst(HttpHeaders.RANGE) != null) {
			Resource resource = (Resource) value;
			try {
				List<HttpRange> httpRanges = inputMessage.getHeaders().getRange();
				outputMessage.getServletResponse().setStatus(HttpStatus.PARTIAL_CONTENT.value());
				outputValue = HttpRange.toResourceRegions(httpRanges, resource);
				valueType = outputValue.getClass();
				declaredType = 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());
			}
		}
	}

	// 设置 响应的header头中的 contentType
	List<MediaType> mediaTypesToUse;

	MediaType contentType = outputMessage.getHeaders().getContentType();
	if (contentType != null && contentType.isConcrete()) {
		mediaTypesToUse = Collections.singletonList(contentType);
	}
	else {
		HttpServletRequest request = inputMessage.getServletRequest();
		List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(request);
		List<MediaType> producibleMediaTypes = getProducibleMediaTypes(request, valueType, declaredType);

		if (outputValue != null && producibleMediaTypes.isEmpty()) {
			throw new HttpMessageNotWritableException(
					"No converter found for return value of type: " + valueType);
		}
		mediaTypesToUse = new ArrayList<>();
		for (MediaType requestedType : requestedMediaTypes) {
			for (MediaType producibleType : producibleMediaTypes) {
				if (requestedType.isCompatibleWith(producibleType)) {
					mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));
				}
			}
		}
		if (mediaTypesToUse.isEmpty()) {
			if (outputValue != null) {
				throw new HttpMediaTypeNotAcceptableException(producibleMediaTypes);
			}
			return;
		}
		MediaType.sortBySpecificityAndQuality(mediaTypesToUse);
	}

	MediaType selectedMediaType = null;
	for (MediaType mediaType : mediaTypesToUse) {
		if (mediaType.isConcrete()) {
			selectedMediaType = mediaType;
			break;
		}
		else if (mediaType.equals(MediaType.ALL) || mediaType.equals(MEDIA_TYPE_APPLICATION)) {
			selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
			break;
		}
	}

	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(declaredType, valueType, selectedMediaType) :
					converter.canWrite(valueType, selectedMediaType)) {
				outputValue = getAdvice().beforeBodyWrite(outputValue, returnType, selectedMediaType,
						(Class<? extends HttpMessageConverter<?>>) converter.getClass(),
						inputMessage, outputMessage);
				if (outputValue != null) {
					addContentDispositionHeader(inputMessage, outputMessage);
					if (genericConverter != null) {
						
						// 写入消息到response中
						genericConverter.write(outputValue, declaredType, selectedMediaType, outputMessage);
					}
					else {
						((HttpMessageConverter) converter).write(outputValue, selectedMediaType, outputMessage);
					}
					if (logger.isDebugEnabled()) {
						logger.debug("Written [" + outputValue + "] as \"" + selectedMediaType +
								"\" using [" + converter + "]");
					}
				}
				return;
			}
		}
	}

	if (outputValue != null) {
		throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
	}
	}
public final void write(final T t, @Nullable final Type type, @Nullable MediaType contentType,
		HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
	
	// 将 contentType 添加到 response 的 header 头中,用于客户端对响应数据的解析
	final HttpHeaders headers = outputMessage.getHeaders();
	addDefaultHeaders(headers, t, contentType);

	if (outputMessage instanceof StreamingHttpOutputMessage) {
		StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) outputMessage;
		streamingOutputMessage.setBody(outputStream -> writeInternal(t, type, new HttpOutputMessage() {
			@Override
			public OutputStream getBody() {
				return outputStream;
			}
			@Override
			public HttpHeaders getHeaders() {
				return headers;
			}
		}));
	}
	else {	
		// 将数据 t 写入到 outputMessage 中
		writeInternal(t, type, outputMessage);
		outputMessage.getBody().flush();
	}
}
protected void addDefaultHeaders(HttpHeaders headers, T t, @Nullable MediaType contentType) throws IOException {
	if (headers.getContentType() == null) {
		MediaType contentTypeToUse = contentType;
		if (contentType == null || contentType.isWildcardType() || contentType.isWildcardSubtype()) {
			contentTypeToUse = getDefaultContentType(t);
		}
		else if (MediaType.APPLICATION_OCTET_STREAM.equals(contentType)) {
			MediaType mediaType = getDefaultContentType(t);
			contentTypeToUse = (mediaType != null ? mediaType : contentTypeToUse);
		}

		// 判断contentType的字符集是否为空,为空就获取默认值(springboot 不同版本,默认值是不同的)
		if (contentTypeToUse != null) {
			if (contentTypeToUse.getCharset() == null) {
				Charset defaultCharset = getDefaultCharset();
				if (defaultCharset != null) {
					contentTypeToUse = new MediaType(contentTypeToUse, defaultCharset);
				}
			}
			headers.setContentType(contentTypeToUse);
		}
	}
	if (headers.getContentLength() < 0 && !headers.containsKey(HttpHeaders.TRANSFER_ENCODING)) {
		Long contentLength = getContentLength(t, headers.getContentType());
		if (contentLength != null) {
			headers.setContentLength(contentLength);
		}
	}
}
protected void writeInternal(Object object, @Nullable Type type, HttpOutputMessage outputMessage)
			throws IOException, HttpMessageNotWritableException {
	
	// 获取在前面设置到 outputMessage 的 header 头中的 contentType
	MediaType contentType = outputMessage.getHeaders().getContentType();
	// 获取 contentType 设置的字符集编码,没有设置,则默认为 UTF-8 
	JsonEncoding encoding = getJsonEncoding(contentType);
	
	// 通过 字符集编码 创建一个 JsonGenerator 
	JsonGenerator generator = this.objectMapper.getFactory().createGenerator(outputMessage.getBody(), encoding);
	try {	
		// 设置前缀
		writePrefix(generator, object);

		Object value = object;
		Class<?> serializationView = null;
		FilterProvider filters = null;
		JavaType javaType = null;

		// 下面就是写入数据
		if (object instanceof MappingJacksonValue) {
			MappingJacksonValue container = (MappingJacksonValue) object;
			value = container.getValue();
			serializationView = container.getSerializationView();
			filters = container.getFilters();
		}
		if (type != null && TypeUtils.isAssignable(type, value.getClass())) {
			javaType = getJavaType(type, null);
		}

		ObjectWriter objectWriter = (serializationView != null ?
				this.objectMapper.writerWithView(serializationView) : this.objectMapper.writer());
		if (filters != null) {
			objectWriter = objectWriter.with(filters);
		}
		if (javaType != null && javaType.isContainerType()) {
			objectWriter = objectWriter.forType(javaType);
		}
		SerializationConfig config = objectWriter.getConfig();
		if (contentType != null && contentType.isCompatibleWith(MediaType.TEXT_EVENT_STREAM) &&
				config.isEnabled(SerializationFeature.INDENT_OUTPUT)) {
			objectWriter = objectWriter.with(this.ssePrettyPrinter);
		}
		objectWriter.writeValue(generator, value);
		
		// 设置后缀
		writeSuffix(generator, object);
		// 将编码后的数据写入输出流
		generator.flush();
	}
	catch (InvalidDefinitionException ex) {
		throw new HttpMessageConversionException("Type definition error: " + ex.getType(), ex);
	}
	catch (JsonProcessingException ex) {
		throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getOriginalMessage(), ex);
	}
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值