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


    参考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,就是该属性储存的 url 与 controller 信息的关系映射

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

    2.2 跟进 AbstractHandlerMethodMapping#initHandlerMethods

    该方法就是将 url 与 RequestMappingInfo(接口方法相关信息) 关系存储到AbstractHandlerMethodMapping.MappingRegistry#urlLookup 字段中,该字段的类型为 MultiValueMap,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());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

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

    protected boolean isHandler(Class<?> beanType) {
    	return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
    			AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
    }
    
    • 1
    • 2
    • 3
    • 4
    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> 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);
    		});
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    以下三个方法,主要是通过 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();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    注册获取到的所有 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();
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    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);
    			}
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    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);
    			}
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87

    从 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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    获取 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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    根据 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();
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    根据请求的 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);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

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

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

    从 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");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

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

    public final boolean supports(Object handler) {
    	return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
    }
    
    protected boolean supportsInternal(HandlerMethod handlerMethod) {
    	return true;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    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;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    这里就会调用我们自己写的 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);
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    我们自己写的 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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    设置请求结果(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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    org.springframework.web.method.HandlerMethod#getResponseStatus

    protected HttpStatus getResponseStatus() {
    	// 默认值为 null
    	return this.responseStatus;
    }
    
    • 1
    • 2
    • 3
    • 4
    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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    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);
    	}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    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();
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    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);
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
  • 相关阅读:
    27 - Excel 的基本公式和重要函数
    常用Win32 API的简单介绍
    【JAVA程序设计】基于SpringBoot+VUE的高校疫情打卡系统-前后端分离
    jenkins声明式流水线pipline深入学习
    JS 对象和函数
    C语言实现八大排序
    CSS选择器分类 [后代选择器、交集选择器、并集选择器(分组选择器)]
    web网页设计期末课程大作业 我的美丽家乡盐城 HTML+CSS+JavaScript
    SpringBoot高频面试题
    在Copernicus Data Space Ecosystem下载Sentinel数据及使用脚本检索和下载数据
  • 原文地址:https://blog.csdn.net/YTREE_BJ/article/details/125781441