接着上一篇请求参数解析,本篇分析讲请求参数解析为对象属性的源码。
demo基于springboot
与上一篇稍微不同,这里的spring版本为5.1.5。
controller
@RestController
@RequestMapping("/index")
public class TestController {
@GetMapping("/test")
public String test(SysUserDto sysUser) {
System.out.println(sysUser.toString());
return "ok";
}
}
接收参数类型为SysUserDto
@Data
public class SysUserDto {
private Integer id;
private String name;
private Integer age;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date birthday;
private SysUserDto userDto;
}
普通类型对象的参数解析器类是ServletModelAttributeMethodProcessor,该类在处理器适配器RequestMappingHandlerAdapter初始化的方法中注册。
先从该类的父类ModelAttributeMethodProcessor#resolveArgument方法开始
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
Assert.state(mavContainer != null, "ModelAttributeMethodProcessor requires ModelAndViewContainer");
Assert.state(binderFactory != null, "ModelAttributeMethodProcessor requires WebDataBinderFactory");
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 {
// Create attribute instance
try {
//创建对象参数实例
attribute = createAttribute(name, parameter, binderFactory, webRequest);
}
catch (BindException ex) {
if (isBindExceptionRequired(parameter)) {
// No BindingResult parameter -> fail with BindException
throw ex;
}
// Otherwise, expose null/empty value and associated BindingResult
if (parameter.getParameterType() == Optional.class) {
attribute = Optional.empty();
}
bindingResult = ex.getBindingResult();
}
}
if (bindingResult == null) {
// Bean property binding and validation;
// skipped in case of binding failure on construction.
//创建数据绑定器
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
if (binder.getTarget() != null) {
if (!mavContainer.isBindingDisabled(name)) {
//绑定请求参数到对象
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);
}
bindingResult = binder.getBindingResult();
}
// Add resolved attribute and BindingResult at the end of the model
Map bindingResultModel = bindingResult.getModel();
mavContainer.removeAttributes(bindingResultModel);
mavContainer.addAllAttributes(bindingResultModel);
return attribute;
}
上面代码中对对象类型参数做了几件事,分别是
1.创建对象实例;
2.创建数据绑定器;
3.绑定请求参数到对象;
这里传递进来的binderFactory是ServletRequestDataBinderFactory,其创建的ExtendedServletRequestDataBinder实例。数据绑定器包含了对象类型以及参数名。
接下来看bindRequestParameters方法,ServletModelAttributeMethodProcessor类重写了该方法
protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest request) {
ServletRequest servletRequest = request.getNativeRequest(ServletRequest.class);
Assert.state(servletRequest != null, "No ServletRequest");
ServletRequestDataBinder servletBinder = (ServletRequestDataBinder) binder;
servletBinder.bind(servletRequest);
}
代码很简单,调用bind方法
ServletRequestDataBinder#bind
public void bind(ServletRequest request) {
//将每个请求参数保存到PropertyValue,并保存为集合
MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request);
//上传文件处理
MultipartRequest multipartRequest = WebUtils.getNativeRequest(request, MultipartRequest.class);
if (multipartRequest != null) {
bindMultipart(multipartRequest.getMultiFileMap(), mpvs);
}
//扩展方法,此处为空
addBindValues(mpvs, request);
//绑定实现
doBind(mpvs);
}
doBind方法最终会调用父类DataBinde的applyPropertyValues方法
protected void doBind(MutablePropertyValues mpvs) {
checkFieldDefaults(mpvs);
checkFieldMarkers(mpvs);
super.doBind(mpvs);
}
protected void doBind(MutablePropertyValues mpvs) {
checkAllowedFields(mpvs);
checkRequiredFields(mpvs);
applyPropertyValues(mpvs);
}
protected void applyPropertyValues(MutablePropertyValues mpvs) {
try {
// Bind request parameters onto target object.
getPropertyAccessor().setPropertyValues(mpvs, isIgnoreUnknownFields(), isIgnoreInvalidFields());
}
catch (PropertyBatchUpdateException ex) {
// Use bind error processor to create FieldErrors.
for (PropertyAccessException pae : ex.getPropertyAccessExceptions()) {
getBindingErrorProcessor().processPropertyAccessException(pae, getInternalBindingResult());
}
}
}
再看看getPropertyAccessor方法,通过名字可以知道,获取属性访问器,通过其setPropertyValues方法设置bean的属性。
getPropertyAccessor
protected ConfigurablePropertyAccessor getPropertyAccessor() {
return getInternalBindingResult().getPropertyAccessor();
}
protected AbstractPropertyBindingResult getInternalBindingResult() {
if (this.bindingResult == null) {
//创建BeanPropertyBindingResult实例
initBeanPropertyAccess();
}
return this.bindingResult;
}
BeanPropertyBindingResult#getPropertyAccessor
public final ConfigurablePropertyAccessor getPropertyAccessor() {
if (this.beanWrapper == null) {
//创建BeanWrapperImpl,其实现了PropertyAccessor接口
this.beanWrapper = createBeanWrapper();
this.beanWrapper.setExtractOldValueForEditor(true);
this.beanWrapper.setAutoGrowNestedPaths(this.autoGrowNestedPaths);
this.beanWrapper.setAutoGrowCollectionLimit(this.autoGrowCollectionLimit);
}
return this.beanWrapper;
}
getPropertyAccessor方法先创建BeanPropertyBindingResult实例,然后调用其getPropertyAccessor方法,其创建BeanWrapperImpl实例,其实现了PropertyAccessor接口。
接着先调用AbstractPropertyAccessor类的setPropertyValues方法,最后通过循环调用AbstractNestablePropertyAccessor类的setPropertyValue方法
public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)
throws BeansException {
List propertyAccessExceptions = null;
List propertyValues = (pvs instanceof MutablePropertyValues ?
((MutablePropertyValues) pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues()));
for (PropertyValue pv : propertyValues) {
try {
// This method may throw any BeansException, which won't be caught
// here, if there is a critical failure such as no matching field.
// We can attempt to deal only with less serious exceptions.
setPropertyValue(pv);
}
省略无关代码...
}
// If we encountered individual exceptions, throw the composite exception.
if (propertyAccessExceptions != null) {
PropertyAccessException[] paeArray = propertyAccessExceptions.toArray(new PropertyAccessException[0]);
throw new PropertyBatchUpdateException(paeArray);
}
}
AbstractNestablePropertyAccessor#setPropertyValue
public void setPropertyValue(PropertyValue pv) throws BeansException {
PropertyTokenHolder tokens = (PropertyTokenHolder) pv.resolvedTokens;
if (tokens == null) {
String propertyName = pv.getName();
AbstractNestablePropertyAccessor nestedPa;
try {
//获取属性的bean,如果是嵌套类,则返回嵌套类的bean
nestedPa = getPropertyAccessorForPropertyPath(propertyName);
}
catch (NotReadablePropertyException ex) {
throw new NotWritablePropertyException(getRootClass(), this.nestedPath + propertyName,
"Nested property in path '" + propertyName + "' does not exist", ex);
}
tokens = getPropertyNameTokens(getFinalPath(nestedPa, propertyName));
if (nestedPa == this) {
pv.getOriginalPropertyValue().resolvedTokens = tokens;
}
//设置参数值
nestedPa.setPropertyValue(tokens, pv);
}
else {
setPropertyValue(tokens, pv);
}
}
getPropertyAccessorForPropertyPath方法比较重要,其通过递归调用处理了嵌套类的情况。
protected AbstractNestablePropertyAccessor getPropertyAccessorForPropertyPath(String propertyPath) {
int pos = PropertyAccessorUtils.getFirstNestedPropertySeparatorIndex(propertyPath);
// Handle nested properties recursively.
if (pos > -1) {
String nestedProperty = propertyPath.substring(0, pos);
String nestedPath = propertyPath.substring(pos + 1);
//生成嵌套类的bean对象
AbstractNestablePropertyAccessor nestedPa = getNestedPropertyAccessor(nestedProperty);
//递归调用嵌套类
return nestedPa.getPropertyAccessorForPropertyPath(nestedPath);
}
else {
return this;
}
}
getNestedPropertyAccessor
private AbstractNestablePropertyAccessor getNestedPropertyAccessor(String nestedProperty) {
if (this.nestedPropertyAccessors == null) {
this.nestedPropertyAccessors = new HashMap<>();
}
// Get value of bean property.
PropertyTokenHolder tokens = getPropertyNameTokens(nestedProperty);
String canonicalName = tokens.canonicalName;
Object value = getPropertyValue(tokens);
if (value == null || (value instanceof Optional && !((Optional) value).isPresent())) {
if (isAutoGrowNestedPaths()) {
//为请求参数对象的属性设置默认值
value = setDefaultValue(tokens);
}
else {
throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + canonicalName);
}
}
// Lookup cached sub-PropertyAccessor, create new one if not found.
AbstractNestablePropertyAccessor nestedPa = this.nestedPropertyAccessors.get(canonicalName);
if (nestedPa == null || nestedPa.getWrappedInstance() != ObjectUtils.unwrapOptional(value)) {
if (logger.isTraceEnabled()) {
logger.trace("Creating new nested " + getClass().getSimpleName() + " for property '" + canonicalName + "'");
}
nestedPa = newNestedPropertyAccessor(value, this.nestedPath + canonicalName + NESTED_PROPERTY_SEPARATOR);
// Inherit all type-specific PropertyEditors.
copyDefaultEditorsTo(nestedPa);
copyCustomEditorsTo(nestedPa, canonicalName);
this.nestedPropertyAccessors.put(canonicalName, nestedPa);
}
else {
if (logger.isTraceEnabled()) {
logger.trace("Using cached nested property accessor for property '" + canonicalName + "'");
}
}
return nestedPa;
}
setDefaultValue
private Object setDefaultValue(PropertyTokenHolder tokens) {
PropertyValue pv = createDefaultPropertyValue(tokens);
setPropertyValue(tokens, pv);
Object defaultValue = getPropertyValue(tokens);
Assert.state(defaultValue != null, "Default value must not be null");
return defaultValue;
}
private PropertyValue createDefaultPropertyValue(PropertyTokenHolder tokens) {
TypeDescriptor desc = getPropertyTypeDescriptor(tokens.canonicalName);
if (desc == null) {
throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + tokens.canonicalName,
"Could not determine property type for auto-growing a default value");
}
Object defaultValue = newValue(desc.getType(), desc, tokens.canonicalName);
return new PropertyValue(tokens.canonicalName, defaultValue);
}
newValue方法为不同类型的属性创建实例
private Object newValue(Class> type, @Nullable TypeDescriptor desc, String name) {
try {
if (type.isArray()) {
Class> componentType = type.getComponentType();
// TODO - only handles 2-dimensional arrays
if (componentType.isArray()) {
Object array = Array.newInstance(componentType, 1);
Array.set(array, 0, Array.newInstance(componentType.getComponentType(), 0));
return array;
}
else {
return Array.newInstance(componentType, 0);
}
}
else if (Collection.class.isAssignableFrom(type)) {
TypeDescriptor elementDesc = (desc != null ? desc.getElementTypeDescriptor() : null);
return CollectionFactory.createCollection(type, (elementDesc != null ? elementDesc.getType() : null), 16);
}
else if (Map.class.isAssignableFrom(type)) {
TypeDescriptor keyDesc = (desc != null ? desc.getMapKeyTypeDescriptor() : null);
return CollectionFactory.createMap(type, (keyDesc != null ? keyDesc.getType() : null), 16);
}
else {
//普通对象,调用反射创建实例
Constructor> ctor = type.getDeclaredConstructor();
if (Modifier.isPrivate(ctor.getModifiers())) {
throw new IllegalAccessException("Auto-growing not allowed with private constructor: " + ctor);
}
return BeanUtils.instantiateClass(ctor);
}
}
catch (Throwable ex) {
throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + name,
"Could not instantiate property type [" + type.getName() + "] to auto-grow nested property path", ex);
}
}
最后第二步,调用AbstractNestablePropertyAccessor类的setPropertyValue方法
protected void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) throws BeansException {
if (tokens.keys != null) {
processKeyedProperty(tokens, pv);
}
else {
//普通属性处理
processLocalProperty(tokens, pv);
}
}
processLocalProperty
private void processLocalProperty(PropertyTokenHolder tokens, PropertyValue pv) {
PropertyHandler ph = getLocalPropertyHandler(tokens.actualName);
if (ph == null || !ph.isWritable()) {
if (pv.isOptional()) {
if (logger.isDebugEnabled()) {
logger.debug("Ignoring optional value for property '" + tokens.actualName +
"' - property not found on bean class [" + getRootClass().getName() + "]");
}
return;
}
else {
throw createNotWritablePropertyException(tokens.canonicalName);
}
}
Object oldValue = null;
try {
Object originalValue = pv.getValue();
Object valueToApply = originalValue;
if (!Boolean.FALSE.equals(pv.conversionNecessary)) {
if (pv.isConverted()) {
valueToApply = pv.getConvertedValue();
}
else {
if (isExtractOldValueForEditor() && ph.isReadable()) {
try {
oldValue = ph.getValue();
}
catch (Exception ex) {
if (ex instanceof PrivilegedActionException) {
ex = ((PrivilegedActionException) ex).getException();
}
if (logger.isDebugEnabled()) {
logger.debug("Could not read previous value of property '" +
this.nestedPath + tokens.canonicalName + "'", ex);
}
}
}
valueToApply = convertForProperty(
tokens.canonicalName, oldValue, originalValue, ph.toTypeDescriptor());
}
pv.getOriginalPropertyValue().conversionNecessary = (valueToApply != originalValue);
}
//完成属性注入
ph.setValue(valueToApply);
}
catch (TypeMismatchException ex) {
throw ex;
}
catch (InvocationTargetException ex) {
PropertyChangeEvent propertyChangeEvent = new PropertyChangeEvent(
getRootInstance(), this.nestedPath + tokens.canonicalName, oldValue, pv.getValue());
if (ex.getTargetException() instanceof ClassCastException) {
throw new TypeMismatchException(propertyChangeEvent, ph.getPropertyType(), ex.getTargetException());
}
else {
Throwable cause = ex.getTargetException();
if (cause instanceof UndeclaredThrowableException) {
// May happen e.g. with Groovy-generated methods
cause = cause.getCause();
}
throw new MethodInvocationException(propertyChangeEvent, cause);
}
}
catch (Exception ex) {
PropertyChangeEvent pce = new PropertyChangeEvent(
getRootInstance(), this.nestedPath + tokens.canonicalName, oldValue, pv.getValue());
throw new MethodInvocationException(pce, ex);
}
}
通过反射注入属性,BeanWrapperImpl#setValue
public void setValue(final @Nullable Object value) throws Exception {
final Method writeMethod = (this.pd instanceof GenericTypeAwarePropertyDescriptor ?
((GenericTypeAwarePropertyDescriptor) this.pd).getWriteMethodForActualAccess() :
this.pd.getWriteMethod());
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction