• 【Spring】@RequestBody的实现原理


    @RequestBody注解可以用于POST请求接收请求体中的参数,使用方式如下:

    @Controller
    public class IndexController {
        
        @PostMapping(value = "/submit", produces = MediaType.APPLICATION_JSON_VALUE)
        public void submit(@RequestBody UserInfo userInfo) {
            System.out.println(userInfo.toString());
        }
    }
    

    那么是如何从请求中解析数据设置到对应的参数中呢,接下来就从源码的角度一探究竟。

    DispatcherServlet是Spring MVC的核心,它对请求进行调度,收到请求后会进入DispatcherServletdoDispatch方法中:

    1. 调用getHandler方法获取请求对应的Handler处理器;
    2. 根据handler获取对应的适配器,这里用到了适配器模式;
    3. 调用适配器的handle方法处理请求,它会返回一个ModelAndView对象;
    public class DispatcherServlet extends FrameworkServlet {
        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 {
    				// 检查是否有Multipart
    				processedRequest = checkMultipart(request);
    				multipartRequestParsed = (processedRequest != request);
    
    				// 根据请求获取对应的处理器
    				mappedHandler = getHandler(processedRequest);
    				if (mappedHandler == null) {
    					noHandlerFound(processedRequest, response);
    					return;
    				}
    
    				// 根据handler获取对应的适配器
    				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    
    				// ...
    
    				// 处理请求
    				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    
    				// ...
    			}
    			catch (Exception ex) {
    				dispatchException = ex;
    			}
    			catch (Throwable err) {
    				dispatchException = new NestedServletException("Handler dispatch failed", err);
    			}
    			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    		}
    		// ...
    	}
    }
    

    通过POSTMAN模拟请求,在代码中打断点可以看到HandlerAdapter的类型为对RequestMappingHandlerAdapter:

    handle方法在其父类AbstractHandlerMethodAdapter中实现,在它的handle方法中,又调用了handleInternal方法处理请求,handleInternal是一个抽象方法,由具体的子类实现:

    public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter, Ordered {
    	@Override
    	@Nullable
    	public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
    			throws Exception {
    	    // 处理请求
    		return handleInternal(request, response, (HandlerMethod) handler);
    	}
    
    	@Nullable
    	protected abstract ModelAndView handleInternal(HttpServletRequest request,
    			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception;
    }
    

    所以回到RequestMappingHandlerAdapterhandleInternal方法,里面调用了invokeHandlerMethod方法进行处理:

    1. 创建ServletInvocableHandlerMethod
    2. 调用invokeAndHandle方法继续请求处理;
    3. 调用getModelAndView方法返回ModelAndView;
    public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
    		implements BeanFactoryAware, InitializingBean {
        @Override
    	protected ModelAndView handleInternal(HttpServletRequest request,
    			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    
    		ModelAndView mav;
    		checkRequest(request);
    		if (this.synchronizeOnSession) {
    			HttpSession session = request.getSession(false);
    			if (session != null) {
    				Object mutex = WebUtils.getSessionMutex(session);
    				synchronized (mutex) {
    					// 执行请求
    					mav = invokeHandlerMethod(request, response, handlerMethod);
    				}
    			}
    			else {
    				// 执行请求
    				mav = invokeHandlerMethod(request, response, handlerMethod);
    			}
    		}
    		else {
    			// 执行请求
    			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 {
    			// ...
                // 创建ServletInvocableHandlerMethod
                ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
    			// 调用invokeAndHandle方法处理请求
    			invocableMethod.invokeAndHandle(webRequest, mavContainer);
    			if (asyncManager.isConcurrentHandlingStarted()) {
    				return null;
    			}
    			// 返回ModelAndView
    			return getModelAndView(mavContainer, modelFactory, webRequest);
    		}
    		finally {
    			webRequest.requestCompleted();
    		}
    	}
    }
    

    ServletInvocableHandlerMethodinvokeAndHandle中调用了invokeForRequest方法执行请求,它的实现在其父类InvocableHandlerMethod中:

    public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
        public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
    			Object... providedArgs) throws Exception {
        	// 执行请求
    		Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
    		setResponseStatus(webRequest);
    
    		// ...
    	}
    }
    

    invokeForRequest中又调用了getMethodArgumentValues方法获取请求中的参数,处理逻辑如下:

    1. 调用getMethodParameters获取方法中的参数,也就是我们的请求处理器方法中的所有参数,上面看到submit只接收了一个UserInfo类型的参数,这里可以从断点中看到parameters中只有一个元素,类型为UserInfo:

    2. 对获取到方法中的所有参数进行遍历,通过处理器调用resolveArgument方法解析请求中的数据,解析每一个参数对应的值;

    public class InvocableHandlerMethod extends HandlerMethod {
    	@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[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
    			Object... providedArgs) throws Exception {
    		// 获取方法的所有参数
    		MethodParameter[] parameters = getMethodParameters();
    		if (ObjectUtils.isEmpty(parameters)) {
    			return EMPTY_ARGS;
    		}
    		Object[] args = new Object[parameters.length];
    		// 对方法中的所有参数进行遍历
    		for (int i = 0; i < parameters.length; i++) {
    			MethodParameter parameter = parameters[i];
    			parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
    			// ...
    			try {
    				// 调用resolveArgument从请求中解析对应的数据
    				args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
    			}
    			// ...
    		}
    		return args;
    	}
    }
    

    resolveArgument方法在HandlerMethodArgumentResolverComposite中实现:

    1. 调用getArgumentResolver方法获取对应的参数处理器resolver

    2. 调用resolverresolveArgument方法进行参数解析;

    public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {
    	@Override
    	@Nullable
    	public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
    			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
            // 获取对应的参数处理器
    		HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
    		if (resolver == null) {
    			throw new IllegalArgumentException("Unsupported parameter type [" +
    					parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
    		}
    		// 解析参数
    		return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
    	}
    }
    

    从断点中可以看到此时的resolverRequestResponseBodyMethodProcessor类型的:

    进入到RequestResponseBodyMethodProcessorresolveArgument方法中,它又调用了readWithMessageConverters方法解析参数,最终会进入到
    AbstractMessageConverterMethodArgumentResolve中的readWithMessageConverters方法:

    public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
        @Override
    	public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
    			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
    
    		parameter = parameter.nestedIfOptional();
    		// 通过转换器进行参数解析
    		Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
    		String name = Conventions.getVariableNameForParameter(parameter);
    		// ...
    		return adaptArgumentIfNecessary(arg, parameter);
    	}
    
    	@Override
    	protected  Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,
    			Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
    
    		HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
    		Assert.state(servletRequest != null, "No HttpServletRequest");
    		ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);
    		// 调用AbstractMessageConverterMethodArgumentResolver中readWithMessageConverters方法读取参数
    		Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
    		if (arg == null && checkRequired(parameter)) {
    			throw new HttpMessageNotReadableException("Required request body is missing: " +
    					parameter.getExecutable().toGenericString(), inputMessage);
    		}
    		return arg;
    	}
    }
    

    readWithMessageConverters方法处理逻辑如下:

    1. 遍历所有HTTP消息转换器,判断是否支持解析当前的请求参数类型;

    2. 如果转换器支持解析当前的参数类型并且有消息体内容,调用转换器的read方法进行解析;

    public abstract class AbstractMessageConverterMethodArgumentResolver implements HandlerMethodArgumentResolver {
        @Nullable
    	protected  Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
    			Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
    
    		// ...
    		try {
    			message = new EmptyBodyCheckingHttpInputMessage(inputMessage);
    			// 遍历所有的消息转换器
    			for (HttpMessageConverter converter : this.messageConverters) {
    				Class> converterType = (Class>) converter.getClass();
    				GenericHttpMessageConverter genericConverter =
    						(converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter) converter : null);
    				// 判断是否支持当前参数类型的读取
    				if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
    						(targetClass != null && converter.canRead(targetClass, contentType))) {
    					// 如果有消息体
    					if (message.hasBody()) {
    						HttpInputMessage msgToUse =
    								getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
    						// 调用read方法进行读取
    						body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
    								((HttpMessageConverter) converter).read(targetClass, msgToUse));
    						body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
    					}
    					else {
    						body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
    					}
    					break;
    				}
    			}
    		}
    		catch (IOException ex) {
    			throw new HttpMessageNotReadableException("I/O error while reading input message", ex, inputMessage);
    		}
    
    		// ...
    		return body;
    	}
    }
    

    这里列举一些消息转换器的类型:

    对于application/json;charset=UTF-8类型会进入到MappingJackson2HttpMessageConverterread方法在其父类AbstractJackson2HttpMessageConverter,处理逻辑如下:

    1. 获取参数的Class类型,从断点中可以看出是[class com.example.demo.model.UserInfo];

    2. 调用readJavaType方法解析参数
      (1)获取ContentType,前面可以看到请求接收的类型为application/json;
      (2)获取字符集,这里的字符集为UTF-8;
      (3)创建ObjectMapper对象,并从请求体中读取JSON数据,转为JAVA对象;

    public abstract class AbstractJackson2HttpMessageConverter extends AbstractGenericHttpMessageConverter {
    
        @Override
    	public Object read(Type type, @Nullable Class contextClass, HttpInputMessage inputMessage)
    			throws IOException, HttpMessageNotReadableException {
    		// 获取参数的Class类型
    		JavaType javaType = getJavaType(type, contextClass);
    		// 解析参数
    		return readJavaType(javaType, inputMessage);
    	}
    
    	private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) throws IOException {
    		// 获取ContentType
    		MediaType contentType = inputMessage.getHeaders().getContentType();
    		// 获取字符集
    		Charset charset = getCharset(contentType);
    		ObjectMapper objectMapper = selectObjectMapper(javaType.getRawClass(), contentType);
    		Assert.state(objectMapper != null, "No ObjectMapper for " + javaType);
    		boolean isUnicode = ENCODINGS.containsKey(charset.name());
    		try {
    		    // ...
    			if (isUnicode) {
    				// 获取HTTP请求体中的JSON数据,转为JAVA对象
    				return objectMapper.readValue(inputMessage.getBody(), javaType);
    			}
    			else {
    				Reader reader = new InputStreamReader(inputMessage.getBody(), charset);
    				return objectMapper.readValue(reader, javaType);
    			}
    		}
    		// ....
    	}
    }
    
    

    到这里已经成功从HTTP请求体中的JSON数据,并转为JAVA对象,完成了参数的设置。

    Spring版本:5.3.4

  • 相关阅读:
    软件工程毕业设计课题(20)基于JAVA毕业设计在线选座购票电影院网站系统毕设作品项目
    TPTU: Task Planning and Tool Usage of Large Language Model-based AI Agents
    高并发下秒杀商品,你必须知道的9个细节
    初识SPDK,从SPDK的软件架构到使用实操
    Java虚拟机(JVM)的调优技巧和实战2
    C#经典十大排序算法(完结)
    python趣味编程-5分钟实现一个简单弹跳球游戏(含源码、步骤讲解)
    Docker Swarm 维护模式
    day7_C++
    RabbitMQ消息可靠性问题
  • 原文地址:https://www.cnblogs.com/shanml/p/17572571.html