【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 ,就是为了下次不用寻找,就是为了更快!