SpringMVC源码:参数解析、方法调用与返回值处理

news2024/11/15 23:35:16

参考资料:

《SpringMVC源码解析系列》

《SpringMVC源码分析》

《Spring MVC源码》

        写在开头:本文为个人学习笔记,内容比较随意,夹杂个人理解,如有错误,欢迎指正。

前文:

《SpringMVC源码:DispatcherServlet初始化流程》

《SpringMVC源码:HandlerMapping加载1》

《SpringMVC源码:HandlerMapping加载2》

《SpringMVC源码:getHandler、getHandlerAdapter过程》

        前文我们已经介绍了,doDispatch方法过程中的handler查找、适配器查找以及拦截器preHandle方法的执行,本文我们会继续介绍方法的调用。

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请求,如果是则HttpServletRequest转换为MultipartHttpServletRequest
            processedRequest = checkMultipart(request);
            multipartRequestParsed = (processedRequest != request);
            //1.根据request信息寻找对应的Handler
            //并返回mappedHeadler,它是一个执行链,包含拦截器和自定义controller
            mappedHandler = getHandler(processedRequest);
            if (mappedHandler == null) {
                //没有找到Handler则通过response反馈错误信息
                noHandlerFound(processedRequest, response);
                return;
            }
            // 2.根据当前的handler寻找对应的HandlerAdapter
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
            // 当前handler处理last-modified
            String method = request.getMethod();
            boolean isGet = "GET".equals(method);
            if (isGet || "HEAD".equals(method)) {
                long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                    return;
                }
            }
            //3.mappedHandler调用拦截器的preHandler方法
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }
            // 4.HandlerAdapter调用自定义Controller中的方法并返回ModelAndView
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
            //5.视图解析,为request请求找到视图
            applyDefaultViewName(processedRequest, mv);
            //6.调用拦截器的postHandle方法
            mappedHandler.applyPostHandle(processedRequest, response, mv);
        }
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }
}

在这里插入图片描述

目录

一、handle

        1、AbstractHandlerMethodAdapter#handle

        2、RequestMappingHandlerAdapter#handleInternal

        3、RequestMappingHandlerAdapter#invokeHandlerMethod

        4、ServletInvocableHandlerMethod#invokeAndHandle

        InvocableHandlerMethod#invokeForRequest

        5、RequestMappingHandlerAdapter#getModelAndView

二、@RequestBody与@ResponseBody

        1、接口转换

         2、参数解析

        2.1、查找解析器

        2.2、解析过程

         3、返回值处理

         3.1、选择处理器

        3.2、处理过程

补充


一、handle

        1、AbstractHandlerMethodAdapter#handle

        这里以AbstractHandlerMethodAdapter类为例,可以看到内部实际上是调用了handleInternal方法,而这个方法实际上是由子类RequestMappingHandlerAdapter实现的。(注意下这里的返回值为ModelAndView)

	@Override
	public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		return handleInternal(request, response, (HandlerMethod) handler);
	}

        2、RequestMappingHandlerAdapter#handleInternal

        该方法主要进行了对请求request的相关校验、解析、转换、执行相关的handler的invokeHandlerMethod 并返回对应的数据视图对象。

	@Override
	protected ModelAndView handleInternal(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
		ModelAndView mav;
        //如果设置supportMethods、requireSession属性需要对其进行校验
        //如果处理的请求方式不是supportMethods集合支持的抛出异常
        //或者requireSession为true但是request不包含session同样抛出异常
		checkRequest(request);


        // 针对线程不安全的session 提供两种方式 一种是同步 从而保证该会话的用户串行执行请求
        // 一种是非同步处理请求 不管同步与否最终调用核心方法invokeHandlerMethod 调用HandlerMethod
		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;
	}

        3、RequestMappingHandlerAdapter#invokeHandlerMethod

        这一步中将HandlerMethod被封装ServletInvocableHandlerMethod,包裹上方法执行需要的信息并调用invokeAndHandle方法执行,最后通过getModelAndView获取视图。

	protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
        //先将请求响应对象包装成ServletWebRequest
		ServletWebRequest webRequest = new ServletWebRequest(request, response);
		try {
            
            //获取该方法的所有使用@InitBinder注解修饰的方法
            //包装成WebDataBinderFactory 在handlerMethod执行前调用(包含类作用范围的和全局作用范围的)
			WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
            //获取该方法的所有使用@ModelAttribute注解修饰的方法 包装成ModelFactory(包含类作用范围的和全局作用范围的)
			ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

            //将HandlerMethod方法包装成ServletInvocableHandlerMethod(该对象是InvocableHandlerMethod子类)
            //设置相关的组件 比如设置参数解析组件argumentResolvers和返回结果处理组件returnValueHandlers
            //绑定组件binderFactory和parameterNameDiscoverer
			ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
			invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
			invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
			invocableMethod.setDataBinderFactory(binderFactory);
			invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

            //创建数据模型和视图的容器对象 见名知义其包含我们所常见的数据和视图对象
			ModelAndViewContainer mavContainer = new ModelAndViewContainer();
            //对于重定向相关的参数保存需要依赖flashMap对象,如果一个请求是重定向请求 则input_flash_map保存重定向入参
            //如果一个请求需要进行重定向 则参数会存放到output_flash_map中
			mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
            //调用@ModelAttribute注解修饰的方法将对应的属性存放到mavContainer 的model中
			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);
			}

            //通过反射执行相关的handlerMethod 涉及参数解析,返回值的处理、
            //invocableMethod已经包含了进行参数解析和返回值处理的组件对象
			invocableMethod.invokeAndHandle(webRequest, mavContainer);
			if (asyncManager.isConcurrentHandlingStarted()) {
				return null;
			}

            //主要处理sessionAttribute 以及对model中的属性进行属性编辑器的转换处理@InitBinder
            //对于view对象进行逻辑视图到物理视图的转换以及重定向参数的设置
			return getModelAndView(mavContainer, modelFactory, webRequest);
		}
		finally {
			webRequest.requestCompleted();
		}
	}

        4、ServletInvocableHandlerMethod#invokeAndHandle

        首先调用invokeForRequest进行参数解析与方法调用,得到返回值后将其封装入mavContainer中。

	public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
        // 请求处理
		Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
        // 返回值处理
        this.returnValueHandlers.handleReturnValue(
					returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
	}

        InvocableHandlerMethod#invokeForRequest

        invokeForRequest方法首先解析参数,然后反射执行方法。

	public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
        //调用getMethodArgumentValues进行参数解析
		Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
        //调用doInvoke方法反射执行handler处理器
		Object returnValue = doInvoke(args);
		return returnValue;
	}
	protected Object doInvoke(Object... args) throws Exception {
		ReflectionUtils.makeAccessible(getBridgedMethod());
		return getBridgedMethod().invoke(getBean(), args);
	}

        5、RequestMappingHandlerAdapter#getModelAndView

        从mavContainer取出结果包装成ModelAndView

	private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
			ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {

		modelFactory.updateModel(webRequest, mavContainer);
		if (mavContainer.isRequestHandled()) {
			return null;
		}
        //从mavContainer中取出model 
		ModelMap model = mavContainer.getModel();
        //根据取出的model创建视图对象
		ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
		if (!mavContainer.isViewReference()) {
			mav.setView((View) mavContainer.getView());
		}
        //处理redirect请求
		if (model instanceof RedirectAttributes) {
			Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
			HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
			RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
		}
		return mav;
	}

二、@RequestBody与@ResponseBody

        以下内容参考自《Spring MVC源码(三) ----- @RequestBody和@ResponseBody原理解析》

        在上文中我们介绍了invokeAndHandle方法,其中的两个步骤分别为参数解析与返回值处理,这一节我们结合两个常用注解@RequestBody与@ResponseBody来进行这部分内容的讲解。

        首先来看个样例,第一个requestBody请求,使用@RequestBody将HTTP请求体转换成String类型,第二个responseBody请求,将Map对象转换成json格式输出到HTTP响应中。

@RequestMapping("/requestBody")
public void requestBody(@RequestBody String body, Writer writer) throws IOException{
    writer.write(body);
}

@RequestMapping(value="/responseBody", produces="application/json")
@ResponseBody
public Map<String, Object> responseBody(){
    Map<String, Object> retMap = new HashMap<>();
    retMap.put("param1", "abc");
    return retMap;
}

         这两个注解都实现了HTTP报文信息同Controller方法中对象的转换,下面我们就来进行原理分析。

        1、接口转换

        SpringMVC中为了实现HTTP消息体和参数及返回值进行转换,所有转换类都实现了HttpMessageConverter接口,该接口定义了5个方法,用于将HTTP请求报文转换为java对象,以及将java对象转换为HTTP响应报文。

public interface HttpMessageConverter<T> {

    // 当前转换器是否能将HTTP报文转换为对象类型
    boolean canRead(Class<?> clazz, MediaType mediaType);

    // 当前转换器是否能将对象类型转换为HTTP报文
    boolean canWrite(Class<?> clazz, MediaType mediaType);

    // 转换器能支持的HTTP媒体类型
    List<MediaType> getSupportedMediaTypes();

    // 转换HTTP报文为特定类型
    T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
            throws IOException, HttpMessageNotReadableException;

    // 将特定类型对象转换为HTTP报文
    void write(T t, MediaType contentType, HttpOutputMessage outputMessage)
            throws IOException, HttpMessageNotWritableException;

}

        对应到SpringMVC的Controller方法,read方法即是读取HTTP请求转换为参数对象,write方法即是将返回值对象转换为HTTP响应报文。SpringMVC定义了两个接口来操作这两个过程:参数解析器HandlerMethodArgumentResolver和返回值处理器HandlerMethodReturnValueHandler。

// 参数解析器接口
public interface HandlerMethodArgumentResolver {

    // 解析器是否支持方法参数
    boolean supportsParameter(MethodParameter parameter);

    // 解析HTTP报文中对应的方法参数
    Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception;

}

// 返回值处理器接口
public interface HandlerMethodReturnValueHandler {

    // 处理器是否支持返回值类型
    boolean supportsReturnType(MethodParameter returnType);

    // 将返回值解析为HTTP响应报文
    void handleReturnValue(Object returnValue, MethodParameter returnType,
            ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;

}

        参数解析器和返回值处理器在底层处理时,都是通过HttpMessageConverter进行转换。流程如下:

        RequestResponseBodyMethodProcessor类实现了HandlerMethodArgumentResolver和HandlerMethodReturnValueHandler两个接口,@RequestBody和@ResponseBody这两个注解的解析均由该类来实现。

         2、参数解析

        在上文中我们介绍了getMethodArgumentValues方法实现了参数解析但并未深入解析,这里我们进行下源码分析

	private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {

		MethodParameter[] parameters = getMethodParameters();
		Object[] args = new Object[parameters.length];
        // 遍历所有参数,逐个解析
		for (int i = 0; i < parameters.length; i++) {
			MethodParameter parameter = parameters[i];
			parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
			args[i] = resolveProvidedArgument(parameter, providedArgs);
			if (args[i] != null) {
				continue;
			}    
            // 通过supportsParameter方法寻找参数合适的解析器
			if (this.argumentResolvers.supportsParameter(parameter)) {
				try {
					args[i] = this.argumentResolvers.resolveArgument(
							parameter, mavContainer, request, this.dataBinderFactory);
					continue;
				}
				catch (Exception ex) {
					if (logger.isDebugEnabled()) {
						logger.debug(getArgumentResolutionErrorMessage("Failed to resolve", i), ex);
					}
					throw ex;
				}
			}
			if (args[i] == null) {
				throw new IllegalStateException("Could not resolve method parameter at index " +
						parameter.getParameterIndex() + " in " + parameter.getMethod().toGenericString() +
						": " + getArgumentResolutionErrorMessage("No suitable resolver for", i));
			}
		}
		return args;
	}

        2.1、查找解析器

        HandlerMethodArgumentResolverComposite类实现了supportsParameter方法,后续调用argumentResolverCache即参数解析器

	private final List<HandlerMethodArgumentResolver> argumentResolvers =
			new LinkedList<HandlerMethodArgumentResolver>();

	@Override
	public boolean supportsParameter(MethodParameter parameter) {
		return getArgumentResolver(parameter) != null;
	}

	private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
		HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
		if (result == null) {
			for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
				if (resolver.supportsParameter(parameter)) {
					result = resolver;
					this.argumentResolverCache.put(parameter, result);
					break;
				}
			}
		}
		return result;
	}

         我们直接进入HandlerMethodArgumentResolver接口的实现类,也正是我们上问提到的RequestResponseBodyMethodProcessor来看看

	@Override
	public boolean supportsParameter(MethodParameter parameter) {
		return parameter.hasParameterAnnotation(RequestBody.class);
	}

         可以看到这里实际上就是判断方法上是否有@RequestBody,这就印证了我们所说的@RequestBody由RequestResponseBodyMethodProcessor来实现解析。

        2.2、解析过程

        已经通过supportsParameter确定了由RequestResponseBodyMethodProcessor类来进行解析,下面再看如何解析,于是我们跟着源码进入resolveArgument方法。

	@Override
	public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, 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);
	}

         其核心内容为通过readWithMessageConverters读取HTTP的请求体内容,而该方法由AbstractMessageConverterMethodArgumentResolver负责实现。

    // RequestResponseBodyMethodProcessor.java
	@Override
	public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {

		parameter = parameter.nestedIfOptional();
        // 读取HTTP报文
		Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
		String name = Conventions.getVariableNameForParameter(parameter);

		WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
		if (arg != null) {
			validateIfApplicable(binder, parameter);
			if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
				throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
			}
		}
		mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());

		return adaptArgumentIfNecessary(arg, parameter);
	}

	@Override
	protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,
			Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {

		HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
		ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);
        //调用AbstractMessageConverterMethodArgumentResolver中的实现
		Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
		if (arg == null) {
			if (checkRequired(parameter)) {
				throw new HttpMessageNotReadableException("Required request body is missing: " +
						parameter.getMethod().toGenericString());
			}
		}
		return arg;
	}

         这里的关键部分即是遍历所有的HttpMessageConverter,通过canRead方法判断转换器是否支持对参数的转换,然后执行read方法完成转换。

protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
        Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {

    ....

    try {
        inputMessage = new EmptyBodyCheckingHttpInputMessage(inputMessage);

        for (HttpMessageConverter<?> converter : this.messageConverters) {
            Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
            ....
            // 判断转换器是否支持参数类型
            if (converter.canRead(targetClass, contentType)) {
                if (inputMessage.getBody() != null) {
                    inputMessage = getAdvice().beforeBodyRead(inputMessage, parameter, targetType, converterType);
                    // read方法执行HTTP报文到参数的转换
                    body = ((HttpMessageConverter<T>) converter).read(targetClass, inputMessage);
                    body = getAdvice().afterBodyRead(body, inputMessage, parameter, targetType, converterType);
                }
                else {
                    body = getAdvice().handleEmptyBody(null, inputMessage, parameter, targetType, converterType);
                }
                break;
            }
            ...
        }
    }
    catch (IOException ex) {
        throw new HttpMessageNotReadableException("I/O error while reading input message", ex);
    }

    ....

    return body;
}

         3、返回值处理

        完成Controller方法的调用后,在ServletInvocableHandlerMethod的invokeAndHandle中,使用返回值处理器对返回值进行转换。

        这里的returnValueHandlers也是HandlerMethodReturnValueHandler的集合体HandlerMethodReturnValueHandlerComposite

	public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
        // 请求处理
		Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
        // 返回值处理
        this.returnValueHandlers.handleReturnValue(
					returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
	}

         3.1、选择处理器

        handleReturnValue首先会通过selectHandler找出支持处理当前返回值的处理器,然后再调用该处理器的handleReturnValue方法进行真正的处理。

    //HandlerMethodReturnValueHandlerComposite.java
	@Override
	public void handleReturnValue(Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
        // 选择合适的HandlerMethodReturnValueHandler,如果没有用@ResposeBody注解和用了注解其返回值处理器肯定不同
		HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
		if (handler == null) {
			throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
		}
        // 执行返回值处理
		handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
	}

        而selectHandler方法遍历所有HandlerMethodReturnValueHandler,调用其supportsReturnType方法选择合适的HandlerMethodReturnValueHandler,然后调用其handleReturnValue方法完成处理。 

    //HandlerMethodReturnValueHandlerComposite.java
	private HandlerMethodReturnValueHandler selectHandler(Object value, MethodParameter returnType) {
		boolean isAsyncValue = isAsyncReturnValue(value, returnType);
		for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
			if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
				continue;
			}
            // 判断当前的HandlerMethodReturnValueHandler是否支持处理返回值
			if (handler.supportsReturnType(returnType)) {
				return handler;
			}
		}
		return null;
	}

         RequestResponseBodyMethodProcessor要求方法上有@ResponseBody注解或者方法所在的Controller类上有@ResponseBody的注解。这就是常常用@RestController注解代替@Controller注解的原因,因为@RestController注解自带@ResponseBody。

	@Override
	public boolean supportsReturnType(MethodParameter returnType) {
		return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
				returnType.hasMethodAnnotation(ResponseBody.class));
	}

        3.2、处理过程

        handleReturnValue方法实际也是调用HttpMessageConverter来完成转换处理

    //RequestResponseBodyMethodProcessor.java
	@Override
	public void handleReturnValue(Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
			throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

		mavContainer.setRequestHandled(true);
		ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
		ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

        // 调用HttpMessageConverter执行
		writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
	}

         AbstractMessageConverterMethodProcessor使用canWrite方法选择合适的HttpMessageConverter,然后调用write方法完成转换。

//AbstractMessageConverterMethodProcessor.java
protected <T> void writeWithMessageConverters(T value, MethodParameter returnType,
        ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
        throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

    ....

    if (selectedMediaType != null) {
        selectedMediaType = selectedMediaType.removeQualityValue();
        for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
            // 判断是否支持返回值类型,返回值类型很有可能不同,如String,Map,List等
            if (messageConverter.canWrite(valueType, selectedMediaType)) {
                outputValue = (T) getAdvice().beforeBodyWrite(outputValue, returnType, selectedMediaType,
                        (Class<? extends HttpMessageConverter<?>>) messageConverter.getClass(),
                        inputMessage, outputMessage);
                if (outputValue != null) {
                    addContentDispositionHeader(inputMessage, outputMessage);
                    // 执行返回值转换
                    ((HttpMessageConverter) messageConverter).write(outputValue, selectedMediaType, outputMessage);
                    ...
                }
                return;
            }
        }
    }
    ....
}

补充

        上文中提到的解析器和转换器都是在适配器初始化的时候添加的。

    //RequestMappingHandlerAdapter.java
	public RequestMappingHandlerAdapter() {
		StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
		stringHttpMessageConverter.setWriteAcceptCharset(false);  // see SPR-7316

		this.messageConverters = new ArrayList<HttpMessageConverter<?>>(4);
		this.messageConverters.add(new ByteArrayHttpMessageConverter());
		this.messageConverters.add(stringHttpMessageConverter);
		this.messageConverters.add(new SourceHttpMessageConverter<Source>());
		this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
	}

	@Override
	public void afterPropertiesSet() {
		// Do this first, it may add ResponseBody advice beans
		initControllerAdviceCache();

		if (this.argumentResolvers == null) {
			List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
			this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
		}
		if (this.initBinderArgumentResolvers == null) {
			List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
			this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
		}
		if (this.returnValueHandlers == null) {
			List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
			this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
		}
	}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/391139.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

并发包中的ConcurrentLinkedQueue和LinkedBlockingQueue有什么区别?

第20讲 | 并发包中的ConcurrentLinkedQueue和LinkedBlockingQueue有什么区别&#xff1f; 在上一讲中&#xff0c;我分析了 Java 并发包中的部分内容&#xff0c;今天我来介绍一下线程安全队列。Java 标准库提供了非常多的线程安全队列&#xff0c;很容易混淆。 今天我要问你的…

谷歌浏览器和火狐浏览器永久禁用缓存【一劳永逸的解决方式】

目录 前言 谷歌浏览器 方式一 方式二 火狐浏览器 前言 缓存对于开发人员来说异常的痛苦,很多莫名其妙的bug就是由缓存导致的,但当我们在网上查找禁用缓存的方式时,找到的方式大多数都是在开发者工具的面板中勾选禁用缓存的选项,但这种方式有个弊端就是需要一直打开这个…

软件测试工程师没有碰到算我输-2023最全的接口测试面试题及参考答案

接口测试最近几年被炒的火热了&#xff0c;越来越多的测试同行意识到接口测试的重要性。接口测试为什么会如此重要呢&#xff1f; 主要是平常的功能点点点&#xff0c;大家水平都一样&#xff0c;是个人都能点&#xff0c;面试时候如果问你平常在公司怎么测试的&#xff0c;你除…

【pytorch】使用mixup技术扩充数据集进行训练

目录1.mixup技术简介2.pytorch实现代码&#xff0c;以图片分类为例1.mixup技术简介 mixup是一种数据增强技术&#xff0c;它可以通过将多组不同数据集的样本进行线性组合&#xff0c;生成新的样本&#xff0c;从而扩充数据集。mixup的核心原理是将两个不同的图片按照一定的比例…

【嵌入式开发】iperf

iperf一级目录用法help文档iperf参数功能iperf测试实例测试网口上行速率测试网口下行速率perf 是一个网络性能测试工具。Iperf可以测试最大TCP和UDP带宽性能&#xff0c;具有多种参数和UDP特性&#xff0c;可以根据需要调整&#xff0c;可以报告带宽、延迟抖动和数据包丢失。一…

小程序项目在hbuilder里面给它打包成app

小程序项目临时有些登录需求&#xff0c;需要把&#xff08;小程序某些功能通过条件编译让它显示到app上&#xff09;小程序打包成app的话就必须需要一个打包的证书&#xff0c;证书的话就要去重新生成&#xff0c;苹果电脑可以去自动生成证书&#xff0c;平时是用windows进行开…

Java的四种引用强软弱虚及其使用场景

一.强引用 回收时机&#xff1a;在内存不足时也不会被回收。 使用方式&#xff1a;String str new String("str"); 使用场景&#xff1a;是平常用的最多的引用 二.软引用 回收时机&#xff1a; 在内存不足时会被回收。 使用方式&#xff1a;SoftRefere…

阿里云全量物理备份.xb备份文件本地恢复

一、下载备份文件并上传到本地 二、安装转码软件 1、yum install https://repo.percona.com/yum/percona-release-latest.noarch.rpm2、yum install percona-xtrabackup-24三、解包并解压 1、先解包 cat test1_qp.xb | xbstream -x -v -C /home/mysql/data 2、然后解压&…

【历史上的今天】3 月 6 日:Unix 版权争夺战;豆瓣网上线;谷歌推出了 Google Play

整理 | 王启隆 透过「历史上的今天」&#xff0c;从过去看未来&#xff0c;从现在亦可以改变未来。 今天是 2023 年 3 月 6 日&#xff0c;在 1475 年的今天&#xff0c;大艺术家米开朗琪罗诞辰。米开朗琪罗是意大利文艺复兴时期的雕塑家、画家&#xff0c;他活到 89 岁&#…

Web服务器基础介绍与Apache的简单介绍(LAMP架构与搭建论坛)

目录 Web服务器基础介绍 一.HTML是什么&#xff1f; 二.静态网页和动态网页 1.静态网页 2.动态网页 3.动态网页语言 三.HTTP协议 1.HTTP协议是什么&#xff1f; 2.HTTP方法 3.HTTP状态码 4.HTTP请求流程分析 4.1 请求报文 4.2 响应报文 Apache的简单介绍 一.Apa…

如何找回回收站删除的视频?这三种方法可以试试

在使用电脑过程中&#xff0c;我们可能会误删重要的文件&#xff0c;特别是影音文件。在这样的情况下&#xff0c;我们可以从计算机的回收站中找回已经被删除的视频。但是有时候&#xff0c;我们可能会不小心清空回收站&#xff0c;这时候就需要一些技巧来恢复回收站删除的视频…

走进JVM

JVM的位置 在操作系统之上&#xff0c;可以想象成一个软件&#xff0c;Java程序都运行在上面 JVM结构图 JVM调优的位置 99%的调优在堆中&#xff0c;极少数在方法区中 很多第三方插件都是在执行引擎那块地方做出修改而来&#xff0c;比如Lombook在程序运行时动态生成get/s…

VSYNC研究

Vsync信号是SurfaceFlinger进程中核心的一块逻辑&#xff0c;我们主要从以下几个方面着手讲解。软件Vsync是怎么实现的&#xff0c;它是如何保持有效性的&#xff1f;systrace中看到的VSYNC信号如何解读&#xff0c;这些脉冲信号是在哪里打印的&#xff1f;为什么VSYNC-sf / VS…

YOLOv5源码逐行超详细注释与解读(1)——项目目录结构解析

前言 前面简单介绍了YOLOv5的网络结构和创新点&#xff08;直通车&#xff1a;【YOLO系列】YOLOv5超详细解读&#xff08;网络详解&#xff09;&#xff09; 在接下来我们会进入到YOLOv5更深一步的学习&#xff0c;首先从源码解读开始。 因为我是纯小白&#xff0c;刚开始下…

【看表情包学Linux】进程创建 | 进程终止 | 分叉函数 fork | 写时拷贝 | 内核数据结构缓冲池 | slab 分派器

爆笑教程《看表情包学Linux》&#x1f448; 猛戳订阅&#xff01;​​​​​&#x1f4ad; 写在前面&#xff1a;本章我们主要讲解进程的创建与终止。首先讲解进程创建&#xff0c;fork 函数是我们早在讲解 "进程的概念" 章节就提到过的一个函数&#xff0c;在上个章…

gma 地理空间绘图:(1) 绘制简单的世界地图-3.设置地图框

内容回顾 gma 地理空间绘图&#xff1a;(1) 绘制简单的世界地图-1.地图绘制与细节调整 gma 地理空间绘图&#xff1a;(1) 绘制简单的世界地图-2.设置经纬网 方法 SetFrame(FrameColor ‘black’, FrameWidth 0.6, ShowFrame True, ShowLeft True, ShowBottom True, Sho…

Golang alpine Dockerfile 最小打包

最近在ubantu 上进行了 iris项目的alpine 版本打包&#xff0c;过程遇到了一些问题&#xff0c;记录一下。 golang版本 &#xff1a;1.18 系统&#xff1a;ubantu 代码结构 Dockfile内容 FROM alpine:latest MAINTAINER Si Wei<3320376695qq.com> ENV VERSION 1.1 ENV G…

格密码学习笔记(二):连续极小、覆盖半径和平滑参数

文章目录最短距离和连续极小值距离函数和覆盖半径格的平滑参数致谢最短距离和连续极小值 除了行列式&#xff0c;格的另一个基本量是格上最短非零向量的长度&#xff0c;即格中最短距离&#xff0c;其定义为 λ1min⁡x,y∈L,x≠y∥x−y∥min⁡z∈L,z≠0∥z∥.\begin{aligned} …

一起来学ASM字节码插桩:从分析class文件结构开始

文章目录Class字节码class字节码构成类型描述符基本类型描述符非数组的引用类型数组引用类型方法描述符OpCode 操作码Class字节码 Java 能做到 一次编译&#xff0c;到处运行&#xff0c;主要就是靠 class字节码 文件&#xff0c;也就是 java 文件经过编译之后 .java -> .c…

【C语言】刷题|链表|双指针|指针|多指针|数据结构

主页&#xff1a;114514的代码大冒 qq:2188956112&#xff08;欢迎小伙伴呀hi✿(。◕ᴗ◕。)✿ &#xff09; Gitee&#xff1a;庄嘉豪 (zhuang-jiahaoxxx) - Gitee.com 文章目录 目录 文章目录 前言 一、移除链表元素 二、反转链表 三&#xff0c;链表的中间结点 四&…