到了请求数据绑定的时候了,很复杂的一个过程;
请求首先到达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方法