springMVC数据绑定

到了请求数据绑定的时候了,很复杂的一个过程;

请求首先到达DispacthServlet的doSerivice方法;然后交由doDispacth处理,方法里几个重要的步骤是

processedRequest = checkMultipart(request);//检查请求是否是文件流
mappedHandler = getHandler(processedRequest, false);//获取请求路径映射
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

//获取当前请求的适配类;

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

//数据绑定和方法被执行,重要的调用方法;

然后到RequestMappingHandlerAdapter的invokeHandlerMethod(...)方法,里面几个重要的调用

requestMappingMethod.invokeAndHandle(webRequest, mavContainer);

该invokeAndHandle方法在ServletInvocableHandlerMethod类中;这个方法的重要调用有

Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs)

该invokeForRequest方法又在InvocableHandlerMethod类中,该方法重要调用是

Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
Object returnValue = invoke(args);

从名称看出,这个调用最终会返回被用户调用方法需要的真实参数;需要说明的是在IoC容器初始化或者被调用的真实controller被实例化的时候,已经保存有该映射路径方法的参数信息,包括方法需要几个参数以及参数类型等。不是在请求时才检查的;

args[i] = argumentResolvers.resolveArgument(parameter, mavContainer, request, dataBinderFactory);

parameter就是被封装的方法参数信息,这是其中某一个;dataBinderFactory是绑定工厂实例,里面包含许多默认类型的转换;跟进这个方法:

String name = ModelFactory.getNameForParameter(parameter);
Object target = (mavContainer.containsAttribute(name)) ?
		mavContainer.getModel().get(name) : createAttribute(name, parameter, binderFactory, request);

name是得到目标方法上当前要绑定参数的名称;target是根据name的类型来生成对象的(也就是说,原生类型不能绑定?注:实践过时可以的,包括数组);

createAttribute方法的最终调用是(当前是业务对象),反射新建一个对象

return BeanUtils.instantiateClass(parameter.getParameterType());

对象创建好了,接下来就是把request的信息绑定到改对象上了;

WebDataBinder binder = binderFactory.createBinder(request, target, name);

会调用到这么个方法;处理标示有@InitBinder的方法;

/**
 * Initialize a WebDataBinder with {@code @InitBinder} methods.
* If the {@code @InitBinder} annotation specifies attributes names, it is
* invoked only if the names include the target object name.
* @throws Exception if one of the invoked @{@link InitBinder} methods fail.
*/
@Override
public void initBinder(WebDataBinder binder, NativeWebRequest request) throws Exception {
		for (InvocableHandlerMethod binderMethod : this.binderMethods) {
			if (isBinderMethodApplicable(binderMethod, binder)) {
				Object returnValue = binderMethod.invokeForRequest(request, null, binder);
				if (returnValue != null) {
					throw new IllegalStateException("@InitBinder methods should return void: " + binderMethod);
				}
			}
		}
	}

this.binderMethods得到的值为:

protected void org.szjz.framework.core.web.base.BaseController.initBinder(javax.servlet.http.HttpServletRequest,org.springframework.web.bind.ServletRequestDataBinder) throws java.lang.Exception

这个是项目中一个基础controller里面的对应的方法为,像是注册Date类型转换,没注意。。。

@InitBinder
protected void initBinder(HttpServletRequest request,
	ServletRequestDataBinder binder) throws Exception {
		binder.registerCustomEditor(Date.class, new DatePropertyEditor());
}

然后就是又是先调用这个方法,那么就走同样的流程;先把该方法的入参绑定;重复以上步骤;该方法处理完后,继续回到调用栈;

WebDataBinder binder = binderFactory.createBinder(request, target, name);
	if (binder.getTarget() != null) {
		bindRequestParameters(binder, request);
		validateIfApplicable(binder, parameter);
		if (binder.getBindingResult().hasErrors()) {
			if (isBindExceptionRequired(binder, parameter)) {
				throw new BindException(binder.getBindingResult());
				}
			}
		}
bindRequestParameters(binder, request);//绑定数据的方法
然后经过几个步骤到达ServletRequestDataBinder类的bind(...)方法
public void bind(ServletRequest request) {
	MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request);//从request中获取得到所有的传值
	MultipartRequest multipartRequest = WebUtils.getNativeRequest(request, MultipartRequest.class);//判断是否是文件流的请求
	if (multipartRequest != null) {
		bindMultipart(multipartRequest.getMultiFileMap(), mpvs);
	}
	addBindValues(mpvs, request);
	doBind(mpvs);
}

doBind方法最终会调用到父类的doBind,父类为DataBinder

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());
		}
	}
}
setPropertyValues方法。。在AbstractPropertyAccessor中
public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)
			throws BeansException {

		List<PropertyAccessException> propertyAccessExceptions = null;
		List<PropertyValue> 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);
		}
.......

setPropertyValue已经是在BeanWrapperImpl中了

@Override
public void setPropertyValue(PropertyValue pv) throws BeansException {
	PropertyTokenHolder tokens = (PropertyTokenHolder) pv.resolvedTokens;
	if (tokens == null) {
		String propertyName = pv.getName();
		BeanWrapperImpl nestedBw;
		try {
			nestedBw = getBeanWrapperForPropertyPath(propertyName);
		}
		catch (NotReadablePropertyException ex) {
			throw new NotWritablePropertyException(getRootClass(), this.nestedPath + propertyName,
					"Nested property in path '" + propertyName + "' does not exist", ex);
		}
		tokens = getPropertyNameTokens(getFinalPath(nestedBw, propertyName));
		if (nestedBw == this) {
			pv.getOriginalPropertyValue().resolvedTokens = tokens;
		}
		nestedBw.setPropertyValue(tokens, pv);
	}
	else {
		setPropertyValue(tokens, pv);
	}
}

又经过一系列的调用到

Object originalValue = pv.getValue();//从PropertyValue取得传值
Object valueToApply = originalValue;
final Method readMethod = pd.getReadMethod();//获取当前属性的get方法
oldValue = readMethod.invoke(object);//确定当前属性的默认值
valueToApply = convertForProperty(propertyName, oldValue, originalValue, pd);

执行属性赋值

最终这个转换赋值过程会调用到TypeConverterDelegate的convertIfNecessary(...)方法

又回到setPropertyValue

final Method writeMethod = (pd instanceof GenericTypeAwarePropertyDescriptor)?((GenericTypeAwarePropertyDescriptor) pd).getWriteMethodForActualAccess() :
pd.getWriteMethod());//得到当前属性的set方法



writeMethod.invoke(this.object, value);//并最终是反射调用set方法





转载于:https://my.oschina.net/u/782865/blog/268340

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值