使用实体类获取请求参数之原理解析

使用实体类获取请求参数之原理解析,第1张

使用实体类获取请求参数之原理解析

文章目录
        • 实例
        • 源码解析
        • 自定义converter
        • 调试

使用实体类接收请求参数,在SpringMVC获取请求参数这篇博文中介绍过,本篇主要解析原理。

实例

看实例先。
新建一个spring项目:demo4。
java目录下新建controller类com.example.boot.controller.Demo4Controller。

package com.example.boot.controller;

import com.example.boot.bean.Person;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class Demo4Controller {
    @PostMapping("/save")
    @ResponseBody
    private Person save(Person person){
        return person;
    }
}

resources.static下新建静态页面index.html。




    
    首页


    


启动应用,提交表单,结果如下。

源码解析

主要看下org.springframework.web.method.annotation.ModelAttributeMethodProcessor#resolveArgument这个方法。

public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
	//...
    Object attribute = null;
    BindingResult bindingResult = null;
    if (mavContainer.containsAttribute(name)) {
		//...
    } else {
        try {
            attribute = this.createAttribute(name, parameter, binderFactory, webRequest);
        } catch (BindException var10) {
        	//...
        }
    }

    if (bindingResult == null) {
        WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
        if (binder.getTarget() != null) {
            if (!mavContainer.isBindingDisabled(name)) {
                this.bindRequestParameters(binder, webRequest);
            }

            this.validateIfApplicable(binder, parameter);
			//...
        }
		//...
        bindingResult = binder.getBindingResult();
    }

    Map bindingResultModel = bindingResult.getModel();
    mavContainer.removeAttributes(bindingResultModel);
    mavContainer.addAllAttributes(bindingResultModel);
    return attribute;
}
  • attribute = this.createAttribute(name, parameter, binderFactory, webRequest),得到一个空的Person类实例,如下。
Person(userName=null, age=null, birth=null, pet=null)
  • WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name),WebDataBinder,即 Web数据绑定器,将请求参数的值绑定到指定的JavaBean里。

    得到的WebDataBinder binder如下。
  • this.bindRequestParameters(binder, webRequest),binder利用它的converters将请求参数转换为指定数据类型,再次封装到JavaBean中。binder.target,就是封装得到JavaBean。

    转换前,首先通过调用 org.springframework.core.convert.support.GenericConversionService#getConverter找converter,用遍历方式找converter。
public GenericConverter find(TypeDescriptor sourceType, TypeDescriptor targetType) {
    List> sourceCandidates = this.getClassHierarchy(sourceType.getType());
    List> targetCandidates = this.getClassHierarchy(targetType.getType());
    Iterator var5 = sourceCandidates.iterator();

    while(var5.hasNext()) {
        Class sourceCandidate = (Class)var5.next();
        Iterator var7 = targetCandidates.iterator();

        while(var7.hasNext()) {
            Class targetCandidate = (Class)var7.next();
            ConvertiblePair convertiblePair = new ConvertiblePair(sourceCandidate, targetCandidate);
            GenericConverter converter = this.getRegisteredConverter(sourceType, targetType, convertiblePair);
            if (converter != null) {
                return converter;
            }
        }
    }

    return null;
}

比如,java.lang.String -> java.lang.Number 时,找到的converter是org.springframework.core.convert.support.StringToNumberConverterFactory。

最后,调用StringToNumberConverterFactory#convert方法,将String转换为Integer。

final class StringToNumberConverterFactory implements ConverterFactory {
    StringToNumberConverterFactory() {
    }

    public  Converter getConverter(Class targetType) {
        return new StringToNumberConverterFactory.StringToNumber(targetType);
    }

    private static final class StringToNumber implements Converter {
        private final Class targetType;

        public StringToNumber(Class targetType) {
            this.targetType = targetType;
        }

        @Nullable
        public T convert(String source) {
            return source.isEmpty() ? null : NumberUtils.parseNumber(source, this.targetType);
        }
    }
}
自定义converter

修改静态页面index.html,修改后的结果如下:




    
    首页


    



改成后,点击提交,显示如下错误页面,且提示Cannot convert value of type ‘java.lang.String’ to required type ‘com.example.boot.bean.Pet’ for property ‘pet’: no matching editors or conversion strategy found。所以,我们需要自定义转换器,可以将String转换为Pet类的转换器。
在com.example.boot下新建配置类config.MyConfig,如下,

package com.example.boot.config;

import com.example.boot.bean.Pet;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.format.FormatterRegistry;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class MyConfig implements WebMvcConfigurer {
    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(new Converter() {
            @Override
            public Pet convert(String source) {
                if(!StringUtils.isEmpty(source)){
                    String[] split = source.split(",");
                    Pet pet = new Pet();
                    pet.setName(split[0]);
                    pet.setWeight(Float.parseFloat(split[1]));
                    return pet;
                }
                return null;
            }
        });
    }
}

实现了WebMvcConfigurer#addFormatters方法,其中定义了converter,该converter可将String类转换成Pet类。

渐渐地,接触WebMvcConfigurer越来越多。

  1. 前后端交互,实现WebMvcConfigurer#addCorsMappings方法,实现跨域。
  2. 基于SpringMVC的web工程的配置实现,使用WebMvcConfigurer实现视图控制器、拦截器、文件上传解析器、静态资源访问等等。
  3. 注解@MatrixVariable,使用WebMvcConfigurer#configurePathMatch方法,实现矩阵变量。
调试

下图演示部分调试过程。

调试过程经过以下部分源码。

  1. org.springframework.web.servlet.DispatcherServlet#doDispatch
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
	//...
	HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
	//...
	mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
	//...
	this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
	//...
}
  1. org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handle
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    return this.handleInternal(request, response, (HandlerMethod)handler);
}
  1. org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInternal
protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
	//...
	mav = this.invokeHandlerMethod(request, response, handlerMethod);
	//...
	return mav;
}
  1. org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
	//...
	invocableMethod.invokeAndHandle(webRequest, mavContainer, new Object[0]);
	//...
	var15 = this.getModelAndView(mavContainer, modelFactory, webRequest);
	//...
	return var15;
}
  1. org.springframework.web.method.support.InvocableHandlerMethod#invokeForRequest
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
    Object[] args = this.getMethodArgumentValues(request, mavContainer, providedArgs);
    if (logger.isTraceEnabled()) {
        logger.trace("Arguments: " + Arrays.toString(args));
    }

    return this.doInvoke(args);
}
  1. org.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
	//...
     Object[] args = new Object[parameters.length];

     for(int i = 0; i < parameters.length; ++i) {
		//...
         if (args[i] == null) {
             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 var10) {
             //...
             }
         }
     }

     return args;
}
  1. org.springframework.web.method.annotation.ModelAttributeMethodProcessor#resolveArgument
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
    //...
    Object attribute = null;
    BindingResult bindingResult = null;
    if (mavContainer.containsAttribute(name)) {
	//...
    } else {
        try {
            attribute = this.createAttribute(name, parameter, binderFactory, webRequest);
        } catch (BindException var10) {
		//...
        }
    }

    if (bindingResult == null) {
        WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
        if (binder.getTarget() != null) {
            if (!mavContainer.isBindingDisabled(name)) {
                this.bindRequestParameters(binder, webRequest);
            }	
            //...
        }
        //...
        bindingResult = binder.getBindingResult();
    }

    Map bindingResultModel = bindingResult.getModel();
    mavContainer.removeAttributes(bindingResultModel);
    mavContainer.addAllAttributes(bindingResultModel);
    return attribute;
}
  1. org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor#bindRequestParameters
protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest request) {
    ServletRequest servletRequest = (ServletRequest)request.getNativeRequest(ServletRequest.class);
    Assert.state(servletRequest != null, "No ServletRequest");
    ServletRequestDataBinder servletBinder = (ServletRequestDataBinder)binder;
    servletBinder.bind(servletRequest);
}
  1. org.springframework.web.bind.ServletRequestDataBinder#bind
public void bind(ServletRequest request) {
    MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request);
    MultipartRequest multipartRequest = (MultipartRequest)WebUtils.getNativeRequest(request, MultipartRequest.class);
    if (multipartRequest != null) {
        this.bindMultipart(multipartRequest.getMultiFileMap(), mpvs);
    } else if (StringUtils.startsWithIgnoreCase(request.getContentType(), "multipart/form-data")) {
        HttpServletRequest httpServletRequest = (HttpServletRequest)WebUtils.getNativeRequest(request, HttpServletRequest.class);
        if (httpServletRequest != null && HttpMethod.POST.matches(httpServletRequest.getMethod())) {
            StandardServletPartUtils.bindParts(httpServletRequest, mpvs, this.isBindEmptyMultipartFiles());
        }
    }

    this.addBindValues(mpvs, request);
    this.doBind(mpvs);
}
  1. org.springframework.web.bind.WebDataBinder#doBind
protected void doBind(MutablePropertyValues mpvs) {
    this.checkFieldDefaults(mpvs);
    this.checkFieldMarkers(mpvs);
    this.adaptEmptyArrayIndices(mpvs);
    super.doBind(mpvs);
}
  1. org.springframework.validation.DataBinder#doBind
protected void doBind(MutablePropertyValues mpvs) {
    this.checkAllowedFields(mpvs);
    this.checkRequiredFields(mpvs);
    this.applyPropertyValues(mpvs);
}
  1. org.springframework.validation.DataBinder#applyPropertyValues
protected void applyPropertyValues(MutablePropertyValues mpvs) {
    try {
        this.getPropertyAccessor().setPropertyValues(mpvs, this.isIgnoreUnknownFields(), this.isIgnoreInvalidFields());
    } catch (PropertyBatchUpdateException var7) {
    //...
    }
}
  1. org.springframework.beans.AbstractPropertyAccessor#setPropertyValues(org.springframework.beans.PropertyValues, boolean, boolean)
public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid) throws BeansException {
	//...
     try {
         Iterator var6 = propertyValues.iterator();

         while(var6.hasNext()) {
             PropertyValue pv = (PropertyValue)var6.next();

             try {
                 this.setPropertyValue(pv);
             } catch (NotWritablePropertyException var14) {
				//...
             } catch (NullValueInNestedPathException var15) {
				//...
             } catch (PropertyAccessException var16) {	
             	//...
             }
         }
     } finally {
		//...
     }
		//...
 }

  1. org.springframework.beans.AbstractNestablePropertyAccessor#setPropertyValue(org.springframework.beans.PropertyValue)
public void setPropertyValue(PropertyValue pv) throws BeansException {
     //...
     nestedPa.setPropertyValue(tokens, pv);
     //...
}
  1. 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);
}
  1. org.springframework.beans.AbstractNestablePropertyAccessor#processLocalProperty
private void processLocalProperty(AbstractNestablePropertyAccessor.PropertyTokenHolder tokens, PropertyValue pv) {
	//...
	valueToApply = this.convertForProperty(tokens.canonicalName, oldValue, originalValue, ph.toTypeDescriptor());
	//...
 }
  1. 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);
}
  1. 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) {
     //...
    }
}
  1. org.springframework.beans.TypeConverterDelegate#convertIfNecessary(java.lang.String, java.lang.Object, java.lang.Object, java.lang.Class, org.springframework.core.convert.TypeDescriptor)
public  T convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue, @Nullable Class requiredType, @Nullable TypeDescriptor typeDescriptor) throws IllegalArgumentException {
	//...
     if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
         try {
             return conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
         } catch (ConversionFailedException var14) {
			//...
         }
     //...
}

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5696697.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存