1. 程式人生 > >SpringBoot MVC實現自定義RequestBody註解

SpringBoot MVC實現自定義RequestBody註解

實現環境:SpringBoot 2.1.1,JDK 1.8

一、MVC實現RequestBody註解原始碼解析

1. @RequestBody

原始碼:

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestBody {

	/**
	 * Whether body content is required.
	 * <p>Default is {@code true}, leading to an exception thrown in case
	 * there is no body content. Switch this to {@code false} if you prefer
	 * {@code null} to be passed when the body content is {@code null}.
	 * @since 3.2
	 */
	boolean required() default true;

}
  • @Target(ElementType.PARAMETER):Target定義註解的作用目標,ElementType.PARAMETER作用在方法引數上
  • @Retention(RetentionPolicy.RUNTIME):註解會在class位元組碼檔案中存在,在執行時可以通過反射獲取到
  • @Documented:說明該註解被包含在javadoc中

2. RequestResponseBodyMethodProcessor

  官方解釋:

Resolves method arguments annotated with {@code @RequestBody} and handles return values from methods annotated with {@code @ResponseBody} by reading and writing to the body of the request or response with an {@link HttpMessageConverter}.

 原始碼中的繼承關係

public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor
public abstract class AbstractMessageConverterMethodProcessor extends AbstractMessageConverterMethodArgumentResolver implements HandlerMethodReturnValueHandler 
  • AbstractMessageConverterMethodProcessor:使用HttpMessageConverters解析response body和request body的引數值
Extends {@link AbstractMessageConverterMethodArgumentResolver} with the ability to handle method return values by writing to the response with {@link HttpMessageConverter HttpMessageConverters}
  • AbstractMessageConverterMethodArgumentResolver:使用HttpMessageConverters解析request body引數值的基類
A base class for resolving method argument values by reading from the body of a request with {@link HttpMessageConverter HttpMessageConverters}.

 RequestResponseBodyMethodProcessor對@RequestBody的繫結及實現原始碼:

public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
    
    ...


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

	/**
	 * Throws MethodArgumentNotValidException if validation fails.
	 * @throws HttpMessageNotReadableException if {@link RequestBody#required()}
	 * is {@code true} and there is no body content or if there is no suitable
	 * converter to read the content with.
	 */
	@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);

		if (binderFactory != null) {
			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());
				}
			}
			if (mavContainer != null) {
				mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
			}
		}

		return adaptArgumentIfNecessary(arg, parameter);
	}

    ...

}
supportsParameter方法實現與@RequestBody繫結,resolveArgument實現具體解析

 二、自定義RequestBody

1. 自定義註解

import java.lang.annotation.*;

@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface KIMBody {
}

2. Resolver

package com.example.kimpay.request;

import org.springframework.core.MethodParameter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver;

import java.util.Iterator;
import java.util.List;

/**
 * Description: 繼承AbstractMessageConverterMethodArgumentResolver實現RequestBody方式解析
 * 也可直接實現HandlerMethodArgumentResolver類自定義解析方式
 * 
 * 
 * 
 */

public class KIMBodyResolver extends AbstractMessageConverterMethodArgumentResolver {
    public KIMBodyResolver(List<HttpMessageConverter<?>> converters) {
        super(converters);
    }

    public KIMBodyResolver(List<HttpMessageConverter<?>> converters, List<Object> requestResponseBodyAdvice) {
        super(converters, requestResponseBodyAdvice);
    }

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(KIMBody.class);//繫結註解
    }

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

        String token=webRequest.getHeader("token");
        //判斷頭部token是否存在
        if (null==token||token.isEmpty()){
            throw new Exception("token is null");
        }
        //列印頭部資訊
        Iterator<String> headerNames = webRequest.getHeaderNames();
        String headerParameter;
        while (headerNames.hasNext()) {
            headerParameter = headerNames.next();
            System.out.println(headerParameter + ":" + webRequest.getHeader(headerParameter));
        }
        //呼叫AbstractMessageConverterMethodArgumentResolver類中readWithMessageConverters方法
        Object args = readWithMessageConverters(webRequest, parameter, parameter.getParameterType());
        return args;
    }
}

3. 將Resolver新增入配置中

package com.example.kimpay.config;

import com.example.kimpay.request.KIMBodyResolver;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.ArrayList;
import java.util.List;

@Configuration
@EnableWebMvc
public class KIMConfig implements WebMvcConfigurer {

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
        //新增訊息轉換器
        converters.add(new MappingJackson2HttpMessageConverter());
        //訊息轉換器與Resolver繫結
        resolvers.add(new KIMBodyResolver(converters));
    }

}

也可以官網上的方式新增配置。

三、HttpMessageConverter<>訊息轉換器

HttpMessageConverter主要是用來轉換request的內容到一定的格式,轉換輸出的內容的到response。即:controller方法返回的型別,可以是位元組陣列、字串、物件引用等,經過HttpMessageConverter處理後,將這些返回型別以一定內容格式(即response的content-type型別,同時還要考慮到客戶端是否接受這個型別)存進response的body中返回給客戶端。可參考https://lgbolgger.iteye.com/blog/2108885

如有謬誤,歡迎各位大佬斧正!