Spring 之请求参数解析原理(实体类传参封装解析)

26 篇文章 1 订阅

Spring 之请求参数解析原理(实体类传参解析)

之前实习的时候有做一个需要反射执行类中方法的组件,对该方法支持的参数类型的映射与解析当时考虑了很多参数类型, 参数类型有 普通类型 数组 实体类 泛型 等更多组合的复杂类型,对于反射执行的方法若重载方法很多,其实是需要确定参数类型来找到对应执行的方法

那么在Spring项目中,对于传参,其实也是可以直接以实体类的形式传参,就很好奇,其是怎么做的?所以看了看源码,分析记录一下

关于Spring的核心元素 DispatchServlet 请查看:Spring 原理之 DispatchServlet 原理

前言

在定义一个接口时,有很多种方式来实现接口的参数接收,常用的有以下三种:

  • request 作为接口的方法参数,然后request 根据 key获取传递的参数值

    @GetMapping("/request_getValue")
    public ResponseEntity<User> requestGetValue(HttpServletRequest request){
        // request 根据 key 获取值
        String username = request.getParameter("username");
        String name = request.getParameter("name");
        User user = new User();
        user.setName(name);
        user.setUsername(username);
        return new ResponseEntity<>(user , HttpStatus.OK);
    }
    
  • 直接以方法参数的方式进接收传递的参数值

    // 直接以参数的形式获取参数值
    @GetMapping("/param_getValue")
    public ResponseEntity<User> paramGetValue(String username,  String name){
        User user = new User();
        user.setName(name);
        user.setUsername(username);
        return new ResponseEntity<>(user , HttpStatus.OK);
    }
    
  • 利用 Pojo 类 以方法参数的方式来封装获取参数值

    @GetMapping("/pojo_getValue")
    public ResponseEntity<User> pojoGetValue(User user){
        return new ResponseEntity<>(user , HttpStatus.OK);
    }
    

那么针对上述的第三种方式,底层是如何进行封装实现的呢?下面来跟踪一下源码,看看具体如何实现的~

原理解析

定位参数处理

关于Spring,不得不提其前端控制器 DispatcherServlet ,所有的请求,都是尤其进行处理的,即调用 DispatcherServlet 中的 **doDispatch() **方法进行处理

流程图如下:(具体的查看 Spring 原理之 DispatchServlet 原理)

请添加图片描述

根据上述流程调试过程经过以下部分源码:

  • 1)org.springframework.web.servlet.DispatcherServlet#doDispatch
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
	//...
    // Determine handler adapter for the current request.
	HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
	//...
	if (!mappedHandler.applyPreHandle(processedRequest, response)) return;
	// Actually invoke the handler.
	mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
	//...
	mappedHandler.applyPostHandle(processedRequest, response, mv);
	//...
	processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
  • 2)org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handle
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
		throws Exception {
	return handleInternal(request, response, (HandlerMethod) handler);
}
  • 3)org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInternal
protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
	//...
	mav = invokeHandlerMethod(request, response, handlerMethod);
	//...
	return mav;
}
  • 4)org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
      HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
   ServletWebRequest webRequest = new ServletWebRequest(request, response);
	 //...
      WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
      ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
      ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
      //...
      invocableMethod.setDataBinderFactory(binderFactory);
	  //...
      ModelAndViewContainer mavContainer = new ModelAndViewContainer();
      mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
      modelFactory.initModel(webRequest, mavContainer, invocableMethod);
      mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
      //...
      // 核心方法 invokeAndHandle
      invocableMethod.invokeAndHandle(webRequest, mavContainer);
      return getModelAndView(mavContainer, modelFactory, webRequest);
    //...
}

说明:

  • RequestMappingHandlerAdapter 已经获取到了 argumentResolvers 列表,用于处理接口参数的处理,后续流程会根据参数类型 找到合适的resolver 进行解析

请添加图片描述

WebDataBinderFactory 用来后续创建 WebDataBinder(数据绑定器,进行数据绑定的工作)

请添加图片描述

其中比较重要的有三个东西(validatorsconversionServiceconverters

1)**validators:**数据校验器,负责 **数据校验 ** 功能,一般用于对使用@Validated 注解参数的校验

2)conversionService:负责数据类型的转换和格式化工作

3)converters :负责各种 数据类型的转换 工作 ,可查看Spring 请求参数类型转换解析(@DateTimeFormat 、自定义Convert)

  • 5)org.springframework.web.servlet.mvc.method.annotation#invokeAndHandle
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
      Object... providedArgs) throws Exception {
   // 核心方法,处理request请求
   Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
   setResponseStatus(webRequest);
   //.....
   mavContainer.setRequestHandled(false);
   //.....
   this.returnValueHandlers.handleReturnValue(
            returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
   //.....
}
  • 6)org.springframework.web.method.support.InvocableHandlerMethod#invokeForRequest

    org.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues

// invokeForRequest
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
    // 获取参数信息进行解析
    Object[] args = this.getMethodArgumentValues(request, mavContainer, providedArgs);
    return this.doInvoke(args);
}

// getMethodArgumentValues
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];
			//.....
             // 找到支持该类型的 resolver(从 argumentResolvers 列表中的resolver一个一个判断)
             // 如果没找到即报错
			if (!this.resolvers.supportsParameter(parameter)) {
				throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
			}
             //.....
             //真正的解析参数
			args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
		}
		return args;
}

上述六个步骤的总结:

  • 首先,对于请求的每一个参数,会逐一进行处理,找到合适的 resolver 来进行处理,默认的resolver有以下26个
    • 0 = {RequestParamMethodArgumentResolver@7782}
      1 = {RequestParamMapMethodArgumentResolver@7783}
      2 = {PathVariableMethodArgumentResolver@7784}
      3 = {PathVariableMapMethodArgumentResolver@7785}
      4 = {MatrixVariableMethodArgumentResolver@7786}
      5 = {MatrixVariableMapMethodArgumentResolver@7787}
      6 = {ServletModelAttributeMethodProcessor@7788}
      7 = {RequestResponseBodyMethodProcessor@7789}
      8 = {RequestPartMethodArgumentResolver@7790}
      9 = {RequestHeaderMethodArgumentResolver@7791}
      10 = {RequestHeaderMapMethodArgumentResolver@7792}
      11 = {ServletCookieValueMethodArgumentResolver@7793}
      12 = {ExpressionValueMethodArgumentResolver@7794}
      13 = {SessionAttributeMethodArgumentResolver@7795}
      14 = {RequestAttributeMethodArgumentResolver@7796}
      15 = {ServletRequestMethodArgumentResolver@7797}
      16 = {ServletResponseMethodArgumentResolver@7798}
      17 = {HttpEntityMethodProcessor@7799}
      18 = {RedirectAttributesMethodArgumentResolver@7800}
      19 = {ModelMethodProcessor@7801}
      20 = {MapMethodProcessor@7802}
      21 = {ErrorsMethodArgumentResolver@7803}
      22 = {SessionStatusMethodArgumentResolver@7804}
      23 = {UriComponentsBuilderMethodArgumentResolver@7805}
      24 = {RequestParamMethodArgumentResolver@7806}
      25 = {ServletModelAttributeMethodProcessor@7807}
    • 上述 resolver 的原理一般是根据参数前是否使用了 指定的注解 来判断是否该 resolver 是否支持解析该参数 或者 同时判断参数的类型是否满足要求
  • 其次,找到合适的 resolver,即调用 resolveArgument 方法进行参数的解析,对于实体类型,这里对应处理的 resolver 为 ModelAttributeMethodProcessor,定位到具体的处理位置

核心处理

上述已经知道了,当传入类型为 Pojo 类时,确定的 resolver 为 ModelAttributeMethodProcessor,下面来看看其是怎么进行封装解析的

  • org.springframework.web.method.annotation.ModelAttributeMethodProcessor#resolveArgument
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
      NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
   // 需要有 ModelAndViewContainer  与  WebDataBinderFactory
   // 获取到参数的key,即参数名称,若标注了ModelAttribute,则获取其中的value值作为该key值
   String name = ModelFactory.getNameForParameter(parameter);
  
   ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
   if (ann != null) {
      mavContainer.setBinding(name, ann.binding());
   }
   Object attribute = null;
   BindingResult bindingResult = null;
   if (mavContainer.containsAttribute(name)) {
      attribute = mavContainer.getModel().get(name);
   }
   else {
      // 1. 创建 attribute
      attribute = createAttribute(name, parameter, binderFactory, webRequest);
      // ...
   }
   if (bindingResult == null) {
      WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
      if (binder.getTarget() != null) {
         if (!mavContainer.isBindingDisabled(name)) {
            // 2. 发送请求的请求参数值进行封装
            bindRequestParameters(binder, webRequest);
         }
         validateIfApplicable(binder, parameter);
         if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
            throw new BindException(binder.getBindingResult());
         }
      }
      // Value type adaptation, also covering java.util.Optional
      if (!parameter.getParameterType().isInstance(attribute)) {
         attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
      }
       // 3. 获取绑定的结果,即绑定后的实体类
      bindingResult = binder.getBindingResult();
   }

   // Add resolved attribute and BindingResult at the end of the model
   Map<String, Object> bindingResultModel = bindingResult.getModel();
   mavContainer.removeAttributes(bindingResultModel);
   mavContainer.addAllAttributes(bindingResultModel);

   return attribute;
}

说明:

  • createAttribute:创建出请求接口中的实体类参数的实体类,若实体类中有传参对应的构造函数,则直接赋予值
  • bindRequestParameters:将发送请求的请求参数值进行解析,与 attribute 的其他属性进行绑定
  • getBindingResult():获取绑定的结果,即上述两个方法完成后最终绑定的实体类
1、createAttribute
  • org.springframework.web.method.annotation.ModelAttributeMethodProcessor#createAttribute
protected Object createAttribute(String attributeName, MethodParameter parameter,
      WebDataBinderFactory binderFactory, NativeWebRequest webRequest) throws Exception {
   // 获取到该参数的详细信息,包括 参数的位置 参数的类型 等相关信息
   MethodParameter nestedParameter = parameter.nestedIfOptional();
   // 获取到该参数的具体类型
   Class<?> clazz = nestedParameter.getNestedParameterType();
   // 找到构造函数这个方法只对Kotlin有用,正常的情况直接返回null
   Constructor<?> ctor = BeanUtils.findPrimaryConstructor(clazz);
   if (ctor == null) {
      // 找到类中的构造函数,如果只有一个就获取一个,不然就获取所有
      Constructor<?>[] ctors = clazz.getConstructors();
      if (ctors.length == 1) {
         ctor = ctors[0];
      }else {
         try {
            ctor = clazz.getDeclaredConstructor();
         }
         catch (NoSuchMethodException ex) {
            throw new IllegalStateException("No primary or default constructor found for " + clazz, ex);
         }
      }
   }
   // 根据构造方法实例化出一个实例
   Object attribute = constructAttribute(ctor, attributeName, parameter, binderFactory, webRequest);
   if (parameter != nestedParameter) {
      attribute = Optional.of(attribute);
   }
   return attribute;
}
  • org.springframework.web.method.annotation.ModelAttributeMethodProcessor#constructAttribute
protected Object constructAttribute(Constructor<?> ctor, String attributeName, MethodParameter parameter,
      WebDataBinderFactory binderFactory, NativeWebRequest webRequest) throws Exception {

   //...
   // 如果没有参数,则通过clazz.newInstance方法实例出这个类
   if (ctor.getParameterCount() == 0) {
      // A single default constructor -> clearly a standard JavaBeans arrangement.
      return BeanUtils.instantiateClass(ctor);
   }
   ConstructorProperties cp = ctor.getAnnotation(ConstructorProperties.class);
   // 根据构造方法获取到该构造方法中的各个参数名称
   String[] paramNames = (cp != null ? cp.value() : parameterNameDiscoverer.getParameterNames(ctor));
   // 根据构造方法获取到该构造方法中的各个参数的类型
   Class<?>[] paramTypes = ctor.getParameterTypes();
   
   Object[] args = new Object[paramTypes.length];
   // 创建 WebDataBinder 数据绑定器,进行数据绑定
   WebDataBinder binder = binderFactory.createBinder(webRequest, null, attributeName);
   String fieldDefaultPrefix = binder.getFieldDefaultPrefix();
   String fieldMarkerPrefix = binder.getFieldMarkerPrefix();
   boolean bindingFailure = false;
   Set<String> failedParams = new HashSet<>(4);
   // 逐一处理构造方法获取到的参数名称,若传参有构造函数对应的key,则保存至args里
   for (int i = 0; i < paramNames.length; i++) {
      String paramName = paramNames[i];
      Class<?> paramType = paramTypes[i];
      // 通过 request.getParameterxxx来获取参数值
      Object value = webRequest.getParameterValues(paramName);
      // 如果没有获取到值(即没有传递该参数),尝试加上前缀 / 后缀来再次获取
      if (value == null) {
         if (fieldDefaultPrefix != null) {
            value = webRequest.getParameter(fieldDefaultPrefix + paramName);
         }
         if (value == null && fieldMarkerPrefix != null) {
            if (webRequest.getParameter(fieldMarkerPrefix + paramName) != null) {
               value = binder.getEmptyValue(paramType);
            }
         }
      }
      try {
         MethodParameter methodParam = new FieldAwareConstructorParameter(ctor, i, paramName);
         // 如果 value 不能空也不是可选参数进行处理
         if (value == null && methodParam.isOptional()) {
            args[i] = (methodParam.getParameterType() == Optional.class ? Optional.empty() : null);
         }else {
            // 通过 WebDataBinder数据绑定器 中的conversionService 中的 converters 进行数据类型转换
            args[i] = binder.convertIfNecessary(value, paramType, methodParam);
         }
      } catch (TypeMismatchException ex) {
         ex.initPropertyName(paramName);
         args[i] = value;
         failedParams.add(paramName);
         binder.getBindingResult().recordFieldValue(paramName, paramType, value);
         binder.getBindingErrorProcessor().processPropertyAccessException(ex, binder.getBindingResult());
         bindingFailure = true;
      }
   }
   // 数据绑定失败即抛出异常
   if (bindingFailure) {
      BindingResult result = binder.getBindingResult();
      for (int i = 0; i < paramNames.length; i++) {
         String paramName = paramNames[i];
         if (!failedParams.contains(paramName)) {
            Object value = args[i];
            result.recordFieldValue(paramName, paramTypes[i], value);
            validateValueIfApplicable(binder, parameter, ctor.getDeclaringClass(), paramName, value);
         }
      }
      throw new BindException(result);
   }
   // 根据构造函数来实例出这个类,args里面的值作为构造函数的传入值,即实现了值得赋予
   return BeanUtils.instantiateClass(ctor, args);
}

总结:上述方法的整个过程就实例化出一个 arttribute(实体类),若实体类中有传参对应的构造函数,则直接赋予值

// 例如有一个实体类User
public class User {
    @NotNull(message = "id不能为空", groups = UpdateUser.class)
    Integer userId;
    @NotBlank(message = "姓名不能为空", groups = CreateUser.class)
    String name;
    String username;
    @Email(message = "不满足邮箱格式")
    private String email;
    // 有一个 userId与email的构造函数
    public User(@NotNull(message = "id不能为空", groups = UpdateUser.class) Integer userId, @Email(message = "不满足邮箱格式") String email) {
        this.userId = userId;
        this.email = email;
    }
}

// 有一个接口
@GetMapping("/pojo_getValue")
public ResponseEntity<User> pojoGetValue(User user){
    return new ResponseEntity<>(user , HttpStatus.OK);
}

// 请求为
http://localhost:8081/pojo_getValue?name=111&username=11122&userId=11
// 这里的Attribute为
User(userId=11, name=null, username=null, email=null)

    
// 请求为
http://localhost:8081/pojo_getValue?name=111&username=11122
// 这里的Attribute为
User(userId=null, name=null, username=null, email=null)
2、bindRequestParameters
  • org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor#bindRequestParameters
@Override
protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest request) {
   ServletRequest servletRequest = request.getNativeRequest(ServletRequest.class);
   Assert.state(servletRequest != null, "No ServletRequest");
   ServletRequestDataBinder servletBinder = (ServletRequestDataBinder) binder;
   // 核心方法,将参数值与实体类中的key绑定起来
   servletBinder.bind(servletRequest);
}
  • org.springframework.web.bind.ServletRequestDataBinder#bind
public void bind(ServletRequest request) {
   // 获取到请求参数的key与value,保存至其 propertyValueList 中,每一个元素包含 参数的名称  参数的值
   MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request);
   MultipartRequest multipartRequest = WebUtils.getNativeRequest(request, MultipartRequest.class);
   if (multipartRequest != null) {
      bindMultipart(multipartRequest.getMultiFileMap(), mpvs);
   }
   // 如果request.getAttribute有传递信息,也加入到 propertyValueList 数组中来
   addBindValues(mpvs, request);
   // 核心方法,进行绑定
   doBind(mpvs);
}
  • org.springframework.web.bind.WebDataBinder#doBind
protected void doBind(MutablePropertyValues mpvs) {
    // 检查是否有默认值,根据前缀检查
    this.checkFieldDefaults(mpvs);
    // 检查是否有默认值,根据后缀检查
    this.checkFieldMarkers(mpvs);
    super.doBind(mpvs);
}
  • org.springframework.validation.DataBinder#doBind
protected void doBind(MutablePropertyValues mpvs) {
    // 同理来检查
    this.checkAllowedFields(mpvs);
    this.checkRequiredFields(mpvs);
    // apply
    this.applyPropertyValues(mpvs);
}
  • org.springframework.validation.DataBinder#applyPropertyValues
protected void applyPropertyValues(MutablePropertyValues mpvs) {
    try {
        this.getPropertyAccessor().setPropertyValues(mpvs, this.isIgnoreUnknownFields(), this.isIgnoreInvalidFields());
    } catch (PropertyBatchUpdateException var7) {
    //...
    }
}
  • org.springframework.beans.AbstractPropertyAccessor#setPropertyValues(org.springframework.beans.PropertyValues, boolean, boolean)
@Override
public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)
      throws BeansException {
   List<PropertyAccessException> propertyAccessExceptions = null;
   // 获取之前保存的请求参数数组 propertyValueList
   List<PropertyValue> propertyValues = (pvs instanceof MutablePropertyValues ?
         ((MutablePropertyValues) pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues()));
   // 逐一进行处理
   for (PropertyValue pv : propertyValues) {
       // 设置值
         setPropertyValue(pv);
    }
}
  • org.springframework.beans.AbstractNestablePropertyAccessor#setPropertyValue(org.springframework.beans.PropertyValue)
public void setPropertyValue(PropertyValue pv) throws BeansException {
     //...
     nestedPa.setPropertyValue(tokens, pv);
     //...
}
  • org.springframework.beans.AbstractNestablePropertyAccessor#setPropertyValue(org.springframework.beans.AbstractNestablePropertyAccessor.PropertyTokenHolder, org.springframework.beans.PropertyValue)
protected void setPropertyValue(AbstractNestablePropertyAccessor.PropertyTokenHolder tokens, PropertyValue pv) throws BeansException {
	//...
    this.processLocalProperty(tokens, pv);
}
  • org.springframework.beans.AbstractNestablePropertyAccessor#processLocalProperty
private void processLocalProperty(PropertyTokenHolder tokens, PropertyValue pv) {
   //...
   Object oldValue = null;
   // 请求参数值
   Object originalValue = pv.getValue();
   Object valueToApply = originalValue;
   if (!Boolean.FALSE.equals(pv.conversionNecessary)) {
         if (pv.isConverted()) {
            valueToApply = pv.getConvertedValue();
         }
         else {
            if (isExtractOldValueForEditor() && ph.isReadable()) {
               // 以前的值
               oldValue = ph.getValue();
               //...
            }
            valueToApply = convertForProperty(tokens.canonicalName, oldValue, originalValue, ph.toTypeDescriptor());
         }
       //...
   }
    // 真正的将转换后的值放入实体类对应的key中
   ph.setValue(valueToApply);
    
}
  • org.springframework.beans.AbstractNestablePropertyAccessor#convertForProperty
protected Object convertForProperty(String propertyName, @Nullable Object oldValue, @Nullable Object newValue, TypeDescriptor td) throws TypeMismatchException {
    return this.convertIfNecessary(propertyName, oldValue, newValue, td.getType(), td);
}
  • org.springframework.beans.AbstractNestablePropertyAccessor#convertIfNecessary
private Object convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue, @Nullable Class<?> requiredType, @Nullable TypeDescriptor td) throws TypeMismatchException {
	//...
    try {
        return this.typeConverterDelegate.convertIfNecessary(propertyName, oldValue, newValue, requiredType, td);
    } catch (IllegalStateException | ConverterNotFoundException var8) {
      //...
    } catch (IllegalArgumentException | ConversionException var9) {
     //...
    }
}
  • org.springframework.beans.TypeConverterDelegate#convertIfNecessary(java.lang.String, java.lang.Object, java.lang.Object, java.lang.Class, org.springframework.core.convert.TypeDescriptor)
public <T> T convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue, @Nullable Class<T> requiredType, @Nullable TypeDescriptor typeDescriptor) throws IllegalArgumentException {
	//...
     if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
         try {
             // 通过 conversionService 中的converters 进行转换
             return conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
         } catch (ConversionFailedException var14) {
			//...
         }
     //...
}

总结:上述这个方法,主要是处理接口的传参参数,逐一处理每个参数,通过与实体类中对应key的类型进行比较,通过converter进行值得转换,转换成功填充值Bean对应得key中,即完成实体类属性值的填充

传参为实体类的总结

接口传参为实体类时,例如:

@GetMapping("/pojo_getValue")
public ResponseEntity<User> pojoGetValue(User user){
    return new ResponseEntity<>(user , HttpStatus.OK);
}

上述的整个核心处理的链路可以简单概括为以下几个部分:

  • 首先确定了处理实体类类型传参的 resolver,为 ModelAttributeMethodProcessor,调用 resolveArgument 方法进行参数的解析
  • 该方法首先,实例出一个实体类User,该实体类的属性初始值与其实现的构造器密切相关:
    • 若只有无参构造器,就是一个值均为null的实体类
    • 若有有参构造器,且接口调用传参有相应的key,则将值通过合适的 converter 进行类型转换后赋予值,若没有相应的key,则初始值为null
  • 其次,该方法逐一处理接口调用的参数,与实体类的属性key对应,通过合适的converter 进行类型转换后赋予值
  • 通过上述两步,实现最终的实体类,进行返回,从而完成封装

Resolver 介绍

根据上述的解析可以得知,参数的解析就是逐一判断参数的解析resolver,即 this.resolvers.supportsParameter(parameter) 是否为 true,默认的 this.resolvers 列表为下面的26个,分别对不同的参数类型进行处理

0 = {RequestParamMethodArgumentResolver@7782} 
1 = {RequestParamMapMethodArgumentResolver@7783} 
2 = {PathVariableMethodArgumentResolver@7784} 
3 = {PathVariableMapMethodArgumentResolver@7785} 
4 = {MatrixVariableMethodArgumentResolver@7786} 
5 = {MatrixVariableMapMethodArgumentResolver@7787} 
6 = {ServletModelAttributeMethodProcessor@7788} 
7 = {RequestResponseBodyMethodProcessor@7789} 
8 = {RequestPartMethodArgumentResolver@7790} 
9 = {RequestHeaderMethodArgumentResolver@7791} 
10 = {RequestHeaderMapMethodArgumentResolver@7792} 
11 = {ServletCookieValueMethodArgumentResolver@7793} 
12 = {ExpressionValueMethodArgumentResolver@7794} 
13 = {SessionAttributeMethodArgumentResolver@7795} 
14 = {RequestAttributeMethodArgumentResolver@7796} 
15 = {ServletRequestMethodArgumentResolver@7797} 
16 = {ServletResponseMethodArgumentResolver@7798} 
17 = {HttpEntityMethodProcessor@7799} 
18 = {RedirectAttributesMethodArgumentResolver@7800} 
19 = {ModelMethodProcessor@7801} 
20 = {MapMethodProcessor@7802} 
21 = {ErrorsMethodArgumentResolver@7803} 
22 = {SessionStatusMethodArgumentResolver@7804} 
23 = {UriComponentsBuilderMethodArgumentResolver@7805} 
24 = {RequestParamMethodArgumentResolver@7806} 
25 = {ServletModelAttributeMethodProcessor@7807} 

说明:

  • 这26个解析参数的类可解析参数可以查看各个类中的 supportsParameter 方法,若返回值为true,即由该类进行解析(大概原理就是判断参数是否使用了 指定的注解 或者 同时判断参数的类型是否满足要求)
  • 若由该类进行解析,则会调用各个类中的 resolveArgument 方法进行核心的参数解析

自定义 ArgumentResolver

如果我不想靠上述默认的一些规则来对传参进行解析,我们可以进行自定义ArgumentResolver

例如:对上述的实体类传参的resolver处理,我们不通过默认的进行处理,而是通过我们自定义的来处理,该如何解决呢?

1、编写 Resolver

上述默认的 26 个 resolver 都直接或者间接实现了 org.springframework.web.method.support.HandlerMethodArgumentResolver 接口,同理,如果我们想要自定义自己的resolver,可以编写 HandlerMethodArgumentResolver 的实现类,重写以下两个方法

  • supportsParameter:是否用该 resolver 进行参数解析,这个方法很关键
  • resolveArgument:参数的解析实现方法
package com.study.config;
import com.study.pojo.User;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

// 自定义 Resolver
public class MyResolver implements HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        // 这里我是判断这个参数是否有我自定义的 MyAnnotation 注解
        return parameter.hasParameterAnnotation(MyAnnotation.class);
        // 也可以换成其他的,例如如果参数的类型是 Student 类的话,用这个resolver解析,怎么用就怎么写
        // return methodParameter.getParameterType().equals(Student.class);
    }
    // 参数解析的具体实现,这里简单获取参数构造成一个实体类返回,可以根据需求写一些自定义的规则实现
    @Override
    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer,
                                  NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
        Integer id = Integer.parseInt(nativeWebRequest.getParameter("id"));
        String email = nativeWebRequest.getParameter("email");
        String username = nativeWebRequest.getParameter("username");
        String name = nativeWebRequest.getHeader("name");
        User user = new User(id, email);
        user.setUsername(username);
        user.setName(name);
        return user.toString();
    }
}


package com.study.config;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String name() default "name";
    boolean required() default  false;
    String defaultValue() default "default_name";
}

2、Resolver注入

这一步保证自定义resolver加入到后续的reslover列表中, 即上述讲解中的的 **this.resolvers.supportsParameter(parameter) ** ,this.resolvers 中

package com.study.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

import java.util.List;

@Configuration
public class MyWebMvcSupport extends WebMvcConfigurationSupport {
	// 将自定义的resolver加入到处理resolver列表中
    @Override
    protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(new MyResolver());
    }
}

3、使用

接口中直接使用

注意点:上述的 MyResolver#resolveArgument 返回的数据类型与这里的接口参数类型要保证一致,如果上述的 resolveArgument 返回的是 user 实体类,而不是user.toString(),这里以 username(字符串)接手就会报错。上述 返回的是 user.toString(),这里的 username 的接受值就为 user 对象的字符串

@GetMapping("/my_own_param_getValue")
public ResponseEntity<User> paramMyOwnValueGetValue(@MyAnnotation String username) {
    User user = new User(1, "1");
    user.setName(username);
    user.setUsername(username);
    return new ResponseEntity<>(user, HttpStatus.OK);
}

4、验证

调用该接口进行调试,可以看到:

  • this.resolvers 列表的数据变为了27个,新增了 自定义 MyResolver
  • 处理该接口参数的 resolver 为 自定义的 MyResolver

请添加图片描述

总结

看了看源码,其实接口传参默认支持的参数类型不多,例如泛型啥的好像都不支持(与泛型类型擦除有关),如果有一些复杂的接口传参类型,可以使用自定义的方法,通过实现 supportsParameterresolveArgument 方法来完成我们的复杂参数解析需求

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值