目录
- 前言
- 源码分析
- 总结
前言
我们在使用springboot开发rest接口时往往是直接写一个接口,然后返回对象,最后结果就转化为Json格式返回了,本文就探究下这个过程中springmvc是如何完成这个过程的。
源码分析
首先我们写了个最简单的接口,并且返回了一个Test对象
@RestController
public class TestController {
@GetMapping("/test")
public Test test() {
Test test = new Test();
test.setName("my name");
return test;
}
}
@Data
public class Test {
private String name;
}
首先我们都知道springmvc最终是由DispatcherServlet来调用到业务代码的,这个过程此处不做介绍,不了解的可以参考此文:
我们直接看DispatcherServlet的doService方法,本文只拉出核心代码,不抓具体细节
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
......
try {
// 核心代码
doDispatch(request, response);
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
......
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
// 核心代码,处理业务流程
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
......
}
打断点进入AbstractHandlerMethodAdapter的handle
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
进入RequestMappingHandlerAdapter的handleInternal
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
checkRequest(request);
// Execute invokeHandlerMethod in synchronized block if required.
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No HttpSession available -> no mutex necessary
// 核心代码,拿到要处理的方法handlerMethod,继续处理业务
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No synchronization on session demanded at all...
mav = invokeHandlerMethod(request, response, handlerMethod);
}
......
return mav;
}
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
// 核心代码1,returnValueHandlers是个集合,包含了很多内置的处理器,根据不同的返回要求处理不同的返回数据
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
invocableMethod.setDataBinderFactory(binderFactory);
......
// 核心代码2,继续处理
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
进入了ServletInvocableHandlerMethod的invokeAndHandle,到这里大概逻辑已经可以看出来了
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 核心代码1,处理请求,返回了returnValue,很明显就是我们调用http请求查询到的Controller
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
// 设置返回状态
setResponseStatus(webRequest);
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
// 核心代码2,处理返回结果
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
// 处理
return doInvoke(args);
}
protected Object doInvoke(Object... args) throws Exception {
ReflectionUtils.makeAccessible(getBridgedMethod());
try {
// 核心代码,这里很明显了,找到要调用的方法,拿到调用的Controller对象,获取到参数,反射调用,然后返回结果
return getBridgedMethod().invoke(getBean(), args);
}
......
}
这里如何根据http请求找到对应的方法,其实原理也很容易理解,无非就是项目启动时把/url和方法的对应关系保存到ioc容器,在需要使用的时候去查询
至此,我们已经拿到Controller相应的方法调用的结果了,然后我们看handleReturnValue方法。
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
// 核心代码1
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if (handler == null) {
throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
}
// 核心代码2
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
@Nullable
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
boolean isAsyncValue = isAsyncReturnValue(value, returnType);
// 循环所有返回值处理器,找可以适配的处理器
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
continue;
}
// 调用每一个处理器的supportsReturnType,直到找到适配的处理器
if (handler.supportsReturnType(returnType)) {
return handler;
}
}
return null;
}
supportsReturnType最终匹配到的是RequestResponseBodyMethodProcessor处理器,我们看下他的supportsReturnType方法
@Override
public boolean supportsReturnType(MethodParameter returnType) {
// 判断类上或者方法上是否存在ResponseBody注解,这里我们类上使用了@RestController注解,@RestController默认是
// 引用了@ResponseBody的注解的,所以这里就可以适配了
return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
returnType.hasMethodAnnotation(ResponseBody.class));
}
然后调用RequestResponseBodyMethodProcessor的handleReturnValue方法
@Override
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.
// 核心代码
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
进入AbstractMessageConverterMethodProcessor的writeWithMessageConverters
protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
Object body;
Class<?> valueType;
Type targetType;
if (value instanceof CharSequence) {
body = value.toString();
valueType = String.class;
targetType = String.class;
}
else {
body = value;
valueType = getReturnValueType(body, returnType);
targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());
}
......
MediaType selectedMediaType = null;
MediaType contentType = outputMessage.getHeaders().getContentType();
if (contentType != null && contentType.isConcrete()) {
if (logger.isDebugEnabled()) {
logger.debug("Found 'Content-Type:" + contentType + "' in response");
}
selectedMediaType = contentType;
}
......
// 当前情况下selectedMediaType最终被设置为application/json;q=0.8
if (selectedMediaType != null) {
selectedMediaType = selectedMediaType.removeQualityValue();
// 核心代码1 messageConverters是处理数据的解析器,从所有解析器中找到匹配的处理器
for (HttpMessageConverter<?> converter : this.messageConverters) {
GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
(GenericHttpMessageConverter<?>) converter : null);
if (genericConverter != null ?
((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
converter.canWrite(valueType, selectedMediaType)) {
// 主要是根据canWrite来判断,最终找到MappingJackson2HttpMessageConverter解析器
body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
(Class<? extends HttpMessageConverter<?>>) converter.getClass(),
inputMessage, outputMessage);
if (body != null) {
Object theBody = body;
LogFormatUtils.traceDebug(logger, traceOn ->
"Writing [" + LogFormatUtils.formatValue(theBody, traceOn) + "]");
addContentDispositionHeader(inputMessage, outputMessage);
if (genericConverter != null) {
genericConverter.write(body, targetType, selectedMediaType, outputMessage);
}
else {
// 最终找到MappingJackson2HttpMessageConverter解析器,执行MappingJackson2HttpMessageConverter的write
((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Nothing to write: null body");
}
}
return;
}
}
}
if (body != null) {
throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
}
}
进入父类AbstractGenericHttpMessageConverter的write
public final void write(final T t, @Nullable final Type type, @Nullable MediaType contentType,
HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
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 {
// 核心代码,write body数据到输出流,里面具体就不细看了
writeInternal(t, type, outputMessage);
// flush
outputMessage.getBody().flush();
}
}
至此,总流程就走完了,数据提供输出流write出去了
总结
梳理下总体流程如下:
- 通过DispatcherServlet根据request的信息找到IOC容器中相应的方法,反射调用
- 根据注解或者返回值类型找到对应的返回值处理器RequestResponseBodyMethodProcessor
- 找到相应的解析器MappingJackson2HttpMessageConverter,这里也就是默认标记了@ResponseBody注解的Controller方法,最终会使用Jackson来进行json序列化,最终返回给客户端