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