springmvc为我们提供了强大的数据绑定功能,但是一直都是只见其行形,不见其意,现在通过阅读源码来深入理解一下具体是怎么实现数据绑定的。
以如下方法为例:两个参数,一个是ModelMap,一个是标记为PathVariable的Integer类型
@RequestMapping(value="/goEdit/{id}")
public String goEdit(ModelMap model,@PathVariable Integer id){
SysActivity sysActivity = sysActivityService.getById(id); //根据ID读取
model.addAttribute("act", sysActivity);
return display("edit");
}
数据绑定的入口顺序是DispatcherServlet类->doService->doDispatch->HandlerAdapter.handle()->RequestMappingHandlerAdapter类的handle方法(此处由父类AbstractHandlerMethodAdapter执行),然后调用handleInternal()->invokeHandleMethod()->invokeAndHandle()->invokeForRequest(),此时开始正式处理方法的参数。
public final Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//开始处理变量
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
StringBuilder sb = new StringBuilder("Invoking [");
sb.append(getBeanType().getSimpleName()).append(".");
sb.append(getMethod().getName()).append("] method with arguments ");
sb.append(Arrays.asList(args));
logger.trace(sb.toString());
}
//方法调用,如getEdit方法开始执行
Object returnValue = invoke(args);
if (logger.isTraceEnabled()) {
logger.trace("Method [" + getMethod().getName() + "] returned [" + returnValue + "]");
}
return returnValue;
}
private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//获取方法所有的参数信息
MethodParameter[] parameters = getMethodParameters();
//根据参数个数创建对应个数的数组,用来存储request中参数的值
Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
GenericTypeResolver.resolveParameterType(parameter, getBean().getClass());
args[i] = resolveProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
if (this.argumentResolvers.supportsParameter(parameter)) {
try {
args[i] = this.argumentResolvers.resolveArgument(
parameter, mavContainer, request, this.dataBinderFactory);
continue;
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(getArgumentResolutionErrorMessage("Error resolving argument", i), ex);
}
throw ex;
}
}
if (args[i] == null) {
String msg = getArgumentResolutionErrorMessage("No suitable resolver for argument", i);
throw new IllegalStateException(msg);
}
}
return args;
}
public Object resolveArgument(
MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
throws Exception {
//获取对应的argumentReslover
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
Assert.notNull(resolver, "Unknown parameter type [" + parameter.getParameterType().getName() + "]");
//根据获取的resolver类型找到对应的执行方法,如下图:
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
因为第一个参数是ModalMap,所以返回的resolver是MapMethodProcessor
public Object resolveArgument(
MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
throws Exception {
return mavContainer.getModel();
}
public ModelMap getModel() {
//判断是不是重定向的参数
if (useDefaultModel()) {
return this.defaultModel;
}
else {
return (this.redirectModel != null) ? this.redirectModel : new ModelMap();
}
}
//BindingAwareModelMap是ModelMap的子类,modelMap是model的实现,所以用model,map,modelMap都可以保模型变量
private final ModelMap defaultModel = new BindingAwareModelMap();
通过上边代码逻辑,最终
同样,第二个参数是Integer,被,@PathVariable标记了,所以返回的参数resolver是PathVariableMethodArgumentResolver
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
//获取uriTemplateVariables中的变量,返回
Map<String, String> uriTemplateVars =
(Map<String, String>) request.getAttribute(
HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
//根据参数名,拿到对应的值
return (uriTemplateVars != null) ? uriTemplateVars.get(name) : null;
}
下边这几个类都是调用父类的resolveArgument方法,然后调用子类的resolveName方法
public final Object resolveArgument(
MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
throws Exception {
Class<?> paramType = parameter.getParameterType();
NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
//执行子类的方法
Object arg = resolveName(namedValueInfo.name, parameter, webRequest);
if (arg == null) {
if (namedValueInfo.defaultValue != null) {
arg = resolveDefaultValue(namedValueInfo.defaultValue);
}
else if (namedValueInfo.required) {
handleMissingValue(namedValueInfo.name, parameter);
}
arg = handleNullValue(namedValueInfo.name, arg, paramType);
}
else if ("".equals(arg) && (namedValueInfo.defaultValue != null)) {
arg = resolveDefaultValue(namedValueInfo.defaultValue);
}
if (binderFactory != null) {
WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
//进行数据类型转换,前台传递都是String类型,需要转换成接收的数据类型
arg = binder.convertIfNecessary(arg, paramType, parameter);
}
handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);
return arg;
}
下边是所有数据类型转换的代码:
public <T> T convertIfNecessary(String propertyName, Object oldValue, Object newValue,
Class<T> requiredType, TypeDescriptor typeDescriptor) throws IllegalArgumentException {
Object convertedValue = newValue;
// Custom editor for this type?
PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);
ConversionFailedException firstAttemptEx = null;
// No custom editor but custom ConversionService specified?
ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
if (editor == null && conversionService != null && convertedValue != null && typeDescriptor != null) {
TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
TypeDescriptor targetTypeDesc = typeDescriptor;
if (conversionService.canConvert(sourceTypeDesc, targetTypeDesc)) {
try {
return (T) conversionService.convert(convertedValue, sourceTypeDesc, targetTypeDesc);
}
catch (ConversionFailedException ex) {
// fallback to default conversion logic below
firstAttemptEx = ex;
}
}
}
// Value not of required type?
if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) {
if (requiredType != null && Collection.class.isAssignableFrom(requiredType) && convertedValue instanceof String) {
TypeDescriptor elementType = typeDescriptor.getElementTypeDescriptor();
if (elementType != null && Enum.class.isAssignableFrom(elementType.getType())) {
convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue);
}
}
if (editor == null) {
editor = findDefaultEditor(requiredType);
}
convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor);
}
boolean standardConversion = false;
if (requiredType != null) {
//
if (convertedValue != null) {
//object类型转换
if (Object.class.equals(requiredType)) {
return (T) convertedValue;
}
if (requiredType.isArray()) {
// 数组转换
if (convertedValue instanceof String && Enum.class.isAssignableFrom(requiredType.getComponentType())) {
convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue);
}
return (T) convertToTypedArray(convertedValue, propertyName, requiredType.getComponentType());
}
else if (convertedValue instanceof Collection) {
// 集合转换
convertedValue = convertToTypedCollection(
(Collection) convertedValue, propertyName, requiredType, typeDescriptor);
standardConversion = true;
}
else if (convertedValue instanceof Map) {
// Map类型转换
convertedValue = convertToTypedMap(
(Map) convertedValue, propertyName, requiredType, typeDescriptor);
standardConversion = true;
}
if (convertedValue.getClass().isArray() && Array.getLength(convertedValue) == 1) {
convertedValue = Array.get(convertedValue, 0);
standardConversion = true;
}
if (String.class.equals(requiredType) && ClassUtils.isPrimitiveOrWrapper(convertedValue.getClass())) {
// We can stringify any primitive value...
return (T) convertedValue.toString();
}
else if (convertedValue instanceof String && !requiredType.isInstance(convertedValue)) {
if (firstAttemptEx == null && !requiredType.isInterface() && !requiredType.isEnum()) {
try {
Constructor<T> strCtor = requiredType.getConstructor(String.class);
return BeanUtils.instantiateClass(strCtor, convertedValue);
}
catch (NoSuchMethodException ex) {
// proceed with field lookup
if (logger.isTraceEnabled()) {
logger.trace("No String constructor found on type [" + requiredType.getName() + "]", ex);
}
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Construction via String failed for type [" + requiredType.getName() + "]", ex);
}
}
}
String trimmedValue = ((String) convertedValue).trim();
if (requiredType.isEnum() && "".equals(trimmedValue)) {
// It's an empty enum identifier: reset the enum value to null.
return null;
}
convertedValue = attemptToConvertStringToEnum(requiredType, trimmedValue, convertedValue);
standardConversion = true;
}
}
if (!ClassUtils.isAssignableValue(requiredType, convertedValue)) {
if (firstAttemptEx != null) {
throw firstAttemptEx;
}
// Definitely doesn't match: throw IllegalArgumentException/IllegalStateException
StringBuilder msg = new StringBuilder();
msg.append("Cannot convert value of type [").append(ClassUtils.getDescriptiveType(newValue));
msg.append("] to required type [").append(ClassUtils.getQualifiedName(requiredType)).append("]");
if (propertyName != null) {
msg.append(" for property '").append(propertyName).append("'");
}
if (editor != null) {
msg.append(": PropertyEditor [").append(editor.getClass().getName()).append(
"] returned inappropriate value of type [").append(
ClassUtils.getDescriptiveType(convertedValue)).append("]");
throw new IllegalArgumentException(msg.toString());
}
else {
msg.append(": no matching editors or conversion strategy found");
throw new IllegalStateException(msg.toString());
}
}
}
if (firstAttemptEx != null) {
if (editor == null && !standardConversion && requiredType != null && !Object.class.equals(requiredType)) {
throw firstAttemptEx;
}
logger.debug("Original ConversionService attempt failed - ignored since " +
"PropertyEditor based conversion eventually succeeded", firstAttemptEx);
}
return (T) convertedValue;
}