SpringBoot中添加@ResponseBody注解会发生什么?

【1】SpringBoot接收到请求

① springboot接收到一个请求返回json格式的列表

将要调用的目标方法如下:

    @ApiOperation(value="分页查询")
    @RequestMapping(value = "/listPage",method = RequestMethod.POST)
    @ResponseBody
    public ResponseBean listPage(@RequestBody JSONObject params){
        Integer pageNum = params.getInteger("pageNum");
        Integer pageSize = params.getInteger("pageSize");
        String vagueParam = params.getString("vagueParam");
        IPage<TbSysGoodsCategory> indexPage = new Page<>(pageNum, pageSize);
        QueryWrapper<TbSysGoodsCategory> queryWrapper = new QueryWrapper<>();
        if (!StringUtils.isEmpty(vagueParam)){
            queryWrapper.like("name",vagueParam).or().like("code",vagueParam);
        }
        //排序
        queryWrapper.orderByDesc("id");
        indexPage = tbSysGoodsCategoryService.page(indexPage,queryWrapper);
        return new ResponseBean<>(true, indexPage, CommonEnum.SUCCESS_OPTION);
    }

如下所示,首先进入DispatcherServlet使用RequestMappingHandlerAdapter进行处理。
在这里插入图片描述
而RequestMappingHandlerAdapter (extends AbstractHandlerMethodAdapter)会调用父类AbstractHandlerMethodAdapter的handle方法进行处理:
在这里插入图片描述

② RequestMappingHandlerAdapter.handleInternal

这里首先在this.checkRequest(request)对请求进行了检测,HttpRequestMethodNotSupportedException异常就是这里抛出的。

	/**
	 * Check the given request for supported methods and a required session, if any.
	 * @param request current HTTP request
	 * @throws ServletException if the request cannot be handled because a check failed
	 * @since 4.2
	 */
	protected final void checkRequest(HttpServletRequest request) throws ServletException {
		// Check whether we should support the request method.
		String method = request.getMethod();
		if (this.supportedMethods != null && !this.supportedMethods.contains(method)) {
			throw new HttpRequestMethodNotSupportedException(method, this.supportedMethods);
		}

		// Check whether a session is required.
		if (this.requireSession && request.getSession(false) == null) {
			throw new HttpSessionRequiredException("Pre-existing session required but none found");
		}
	}

其他没有什么需要特殊说明的,直接调用了invokeHandlerMethod方法进行实际业务处理。
在这里插入图片描述


【2】RequestMappingHandlerAdapter.invokeHandlerMethod核心处理

RequestMappingHandlerAdapter.invokeHandlerMethod

这个方法十分重要,是请求处理流程中的核心方法。这个方法会根据handlerMethod获取一个ServletInvocableHandlerMethod 并对其进行各种属性设置然后调用其invokeAndHandle方法进行处理。

    @Nullable
    protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    // 对应 2 
        ServletWebRequest webRequest = new ServletWebRequest(request, response);

        Object result;
        try {
        // 对应 3
            WebDataBinderFactory binderFactory = this.getDataBinderFactory(handlerMethod);
   // 对应 4
            ModelFactory modelFactory = this.getModelFactory(handlerMethod, binderFactory);
   // 对应 5
            ServletInvocableHandlerMethod invocableMethod = this.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);
 // 对应 6
            ModelAndViewContainer mavContainer = new ModelAndViewContainer();
            mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
            modelFactory.initModel(webRequest, mavContainer, invocableMethod);
            mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
//对应 7
            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()) {
                result = asyncManager.getConcurrentResult();
                mavContainer = (ModelAndViewContainer)asyncManager.getConcurrentResultContext()[0];
                asyncManager.clearConcurrentResult();
                LogFormatUtils.traceDebug(this.logger, (traceOn) -> {
                    String formatted = LogFormatUtils.formatValue(result, !traceOn);
                    return "Resume with async result [" + formatted + "]";
                });
                invocableMethod = invocableMethod.wrapConcurrentResult(result);
            }
//这里会跳到【3】ServletInvocableHandlerMethod.invokeAndHandle
            invocableMethod.invokeAndHandle(webRequest, mavContainer, new Object[0]);
            if (!asyncManager.isConcurrentHandlingStarted()) {
//这里会跳到【4】RequestMappingHandlerAdapter.getModelAndView
                ModelAndView var15 = this.getModelAndView(mavContainer, modelFactory, webRequest);
                return var15;
            }

            result = null;
        } finally {
            webRequest.requestCompleted();
        }

        return (ModelAndView)result;
    }

① 此时的handlerMethod是什么?

如下图所示:
在这里插入图片描述
② 此时的ServletWebRequest webRequest是什么?

如下图所示:
在这里插入图片描述


③ 此时的WebDataBinderFactory binderFactory

WebDataBinderFactory binderFactory = this.getDataBinderFactory(handlerMethod);

在这里插入图片描述

这个很关键。SpringBoot请求处理流程中最重要的一步就是数据绑定,即将参数写到目标对象上面。那么这里就涉及到参数校验、数据格式转换、绑定结果对象、错误对象等。
在这里插入图片描述
其属性ConfigurableWebBindingInitializer对象提供了基础功能,该对象中WebConversionService中转换器实例如下:
在这里插入图片描述

④ 根据handlerMethod和binderFactory获取到ModelFactory modelFactory

 ModelFactory modelFactory = this.getModelFactory(handlerMethod, binderFactory);

可以看到modelFactory主要属性dataBindFactory和sessionAttributeHandler都是为参数绑定数据服务的。
在这里插入图片描述


⑤ 创建核心处理对象ServletInvocableHandlerMethod invocableMethod并为其属性赋值

 ServletInvocableHandlerMethod invocableMethod = this.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);

argumentResolvers 看名字就应该知道是为参数解析进行服务的,其值列表如下:
在这里插入图片描述
我想你现在应该知道为什么方法参数使用@RequestBody就可以进行参数绑定了吧!

继续看returnValueHandlers,也就是返回结果处理器。其中returnValueHandlers是HandlerMethodReturnValueHandlerComposite实例,就像HandlermethodArgumentResolverComposite一样,它包含了所有HandlerMethodReturnValueHandler的列表,并在Spring启动时完成注册。其值列表如下:
在这里插入图片描述
ok,我们的主题来了。就是这个RequestResponseBodyMethodProcessor后置处理器对@ResponseBody注解进行的处理!

继续往下走,给invocableMethod设置了DataBinderFactory。这个同上都是为数据参数绑定服务,继续往下看invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);parameterNameDiscoverer这里值列表如下:

在这里插入图片描述


⑥ 创建mavContainer进行数据的初步处理

//创建ModelAndViewContainer 实例对象
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
//从请求中获取InputFlashMap并把其数据放入defaultModel中,flashmap的作用是在redirect中传递参数
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
//调用modelFactory对model进行初始化
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
//重定向时忽略默认Model
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

首先看下ModelAndViewContainer,其核心有三个属性view-视图对象,defaultModel-默认的数据存放地方以及redirectModel-重定向时数据存放地方。
在这里插入图片描述

modelFactory.initModel(webRequest, mavContainer, invocableMethod);,这里对model做了处理。也可以说是对目标方法实际调用前对数据做的最后一次处理:

public void initModel(NativeWebRequest request, ModelAndViewContainer container, HandlerMethod handlerMethod)
			throws Exception {
	//尝试获取会话中保存的属性值
		Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
		//对model中属性值进行合并处理:称之为补缺更合适
		container.mergeAttributes(sessionAttributes);
		
		//调用标注了@ModelAttribute注解的方法
		invokeModelAttributeMethods(request, container);

		//如果handlerMethod的方法参数标注了@ModelAttribute注解并且在sessionAttributetes存在,则对其进行遍历
		//尝试获取值,如果获取不到值就会抛出异常;如果获取到值就会放到model-defaultModel中
		for (String name : findSessionAttributeArguments(handlerMethod)) {
			if (!container.containsAttribute(name)) {
				Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
				if (value == null) {
					throw new HttpSessionRequiredException("Expected session attribute '" + name + "'", name);
				}
				container.addAttribute(name, value);
			}
		}
	}

关于mergeAttributes合并属性方法源码如下:

#也就是说遍历sessionAttributes ,如果model中不存在,就放入。如果存在,就跳过!注意,不会进行值覆盖
 public ModelMap mergeAttributes(@Nullable Map<String, ?> attributes) {
        if (attributes != null) {
            attributes.forEach((key, value) -> {
                if (!this.containsKey(key)) {
                    this.put(key, value);//this这里值的是modelMap,也就是defaultModel
                }

            });
        }
        return this;
    }

关于findSessionAttributeArguments方法源码如下:

	 //从方法参数中找到在(@SessionAttributes注解的属性/参数)中存在的
	 // 且方法参数上标注了@ModelAttribute注解的
	private List<String> findSessionAttributeArguments(HandlerMethod handlerMethod) {
		List<String> result = new ArrayList<>();
		for (MethodParameter parameter : handlerMethod.getMethodParameters()) {
			if (parameter.hasParameterAnnotation(ModelAttribute.class)) {
				String name = getNameForParameter(parameter);
				Class<?> paramType = parameter.getParameterType();
				if (this.sessionAttributesHandler.isHandlerSessionAttribute(name, paramType)) {
					result.add(name);
				}
			}
		}
		return result;
	}

⑦ 异步请求

这一块先不用管,后续分析

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();
	LogFormatUtils.traceDebug(logger, traceOn -> {
		String formatted = LogFormatUtils.formatValue(result, !traceOn);
		return "Resume with async result [" + formatted + "]";
	});
	invocableMethod = invocableMethod.wrapConcurrentResult(result);
}

接下来调用invocableMethod.invokeAndHandle(webRequest, mavContainer);就到了ServletInvocableHandlerMethod.invokeAndHandle。


【3】调用目标方法并对返回值进行处理ServletInvocableHandlerMethod.invokeAndHandle

方法源码如下:

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
//调用目标方法并获取返回值,这里对应 【3.1】 InvocableHandlerMethod.invokeForRequest调用目标方法
		Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
		setResponseStatus(webRequest);

		if (returnValue == null) {
			if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
				disableContentCachingIfNecessary(webRequest);
				mavContainer.setRequestHandled(true);
				return;
			}
		}
		else if (StringUtils.hasText(getResponseStatusReason())) {
			mavContainer.setRequestHandled(true);
			return;
		}

		mavContainer.setRequestHandled(false);
		Assert.state(this.returnValueHandlers != null, "No return value handlers");
		try {
			this.returnValueHandlers.handleReturnValue(
					returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
		}
		catch (Exception ex) {
			if (logger.isTraceEnabled()) {
				logger.trace(formatErrorForReturnValue(returnValue), ex);
			}
			throw ex;
		}
	}

【3.1】 InvocableHandlerMethod.invokeForRequest调用目标方法

public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
		Object... providedArgs) throws Exception {
//解析参数--这里对应 1
	Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
	if (logger.isTraceEnabled()) {
		logger.trace("Arguments: " + Arrays.toString(args));
	}
	//根据上面得到的参数值调用目标方法  这里对应2 
	return doInvoke(args);
}

① 解析参数getMethodArgumentValues

protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
//获取到方法的参数对象   MethodParameter[]数组
		MethodParameter[] parameters = getMethodParameters();

//如果为空,返回空参数组
		if (ObjectUtils.isEmpty(parameters)) {
			return EMPTY_ARGS;
		}
		Object[] args = new Object[parameters.length];
		
		//遍历MethodParameter[] parameters,对每一个方法参数对象获取到具体参数并解析得到参数值
		for (int i = 0; i < parameters.length; i++) {
			MethodParameter parameter = parameters[i];
			//绑定参数名称发现器
			parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
			//从providedArgs中尝试获取到参数名
			args[i] = findProvidedArgument(parameter, providedArgs);
			if (args[i] != null) {
				continue;
			}
			//如果方法参数解析器不支持parameter,则抛出异常
			if (!this.resolvers.supportsParameter(parameter)) {
				throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
			}
			try {
			//
				args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
			}
			catch (Exception ex) {
				// Leave stack trace for later, exception may actually be resolved and handled...
				if (logger.isDebugEnabled()) {
					String exMsg = ex.getMessage();
					if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
						logger.debug(formatArgumentError(parameter, exMsg));
					}
				}
				throw ex;
			}
		}
		return args;
	}

MethodParameter[] parameters = getMethodParameters();这里获取的 MethodParameter[] parameters如下图所示:
在这里插入图片描述
参数解析器组合对象( this.resolvers)列表如下所示:
在这里插入图片描述

为什么称之为参数解析器组合对象?其实这里的this.resolvers并不是具体的参数解析器而是argumentResolvers、argumentResolverCache组合而成的HandlerMethodArgumentResolverComposite!

可以看到起还有argumentResolverCache属性,其值列表如下:
在这里插入图片描述
默认argumentResolverCache是一个容量为256的ConcurrentHashMap,是HandlerMethodArgumentResolverComposite的成员变量:

private final Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache =
			new ConcurrentHashMap<>(256);

这个argumentResolverCache是在动态改变,其在判断是否支持paramter的方法中会改变,HandlerMethodArgumentResolverComposite.getArgumentResolver源码如下:

private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
//如果缓存中有,则直接返回
		HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
		//如果缓存中没有就尝试从解析器列表中获取一个支持parameter的,并将解析器 parameter放入缓存
		if (result == null) {
			for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
				if (resolver.supportsParameter(parameter)) {
					result = resolver;
					this.argumentResolverCache.put(parameter, result);
					break;
				}
			}
		}
		return result;
	}

为什么要有argumentResolverCache ?你可以没有,但是你就需要每次从argumentResolvers遍历寻找支持当前MethodParameter的参数解析器!之所以保存一份键值对数据到argumentResolverCache ,就是为了下次不用寻找,就是为了更快!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章