博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
springMVC数据绑定
阅读量:6294 次
发布时间:2019-06-22

本文共 6524 字,大约阅读时间需要 21 分钟。

hot3.png

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

请求首先到达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.*/@Overridepublic 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类型转换,没注意。。。

@InitBinderprotected 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
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); }.......

setPropertyValue已经是在BeanWrapperImpl中了

@Overridepublic 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

你可能感兴趣的文章
Sphinx 配置文件说明
查看>>
数据结构实践——顺序表应用
查看>>
python2.7 之centos7 安装 pip, Scrapy
查看>>
机智云开源框架初始化顺序
查看>>
Spark修炼之道(进阶篇)——Spark入门到精通:第五节 Spark编程模型(二)
查看>>
一线架构师实践指南:云时代下双活零切换的七大关键点
查看>>
ART世界探险(19) - 优化编译器的编译流程
查看>>
玩转Edas应用部署
查看>>
music-音符与常用记号
查看>>
sql操作命令
查看>>
zip 数据压缩
查看>>
Python爬虫学习系列教程
查看>>
【数据库优化专题】MySQL视图优化(二)
查看>>
【转载】每个程序员都应该学习使用Python或Ruby
查看>>
PHP高级编程之守护进程,实现优雅重启
查看>>
PHP字符编码转换类3
查看>>
rsync同步服务配置手记
查看>>
http缓存知识
查看>>
Go 时间交并集小工具
查看>>
iOS 多线程总结
查看>>