普通参数绑定源码分析
1.参数解析器:
@Override
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
//遍历参数处理器解析器, 判断我们哪个解析器可以处理我们的这个参数
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
if (resolver == null) {
throw new IllegalArgumentException("Unsupported parameter type [" +
parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
}
//调用resolverArgument方法完成参数的绑定,将参数进行绑定
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
Model和map参数解析器
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
Assert.state(mavContainer != null, "ModelAndViewContainer is required for model exposure");
//调用此方法,获取ModelAndViewContainer类型
return mavContainer.getModel();
}
执行完目标方法之后,又在下面返回值处理器的处理中,设置了我们的view
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
//处理派发请求
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
rander 下的
view.rander 渲染视图
//这个方法用于将我们的ModelMap类型转化为Map类型,暴漏模型的时候传入使用
Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
//暴漏模型,将我们的参数填充到请求域中
renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
//暴漏模型
@Override
protected void renderMergedOutputModel(
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Expose the model object as request attributes.
exposeModelAsRequestAttributes(model, request);
// Expose helpers as request attributes, if any.
exposeHelpers(request);
// Determine the path for the request dispatcher.
String dispatcherPath = prepareForRendering(request, response);
// Obtain a RequestDispatcher for the target resource (typically a JSP).
RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
if (rd == null) {
throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +
"]: Check that the corresponding file exists within your web application archive!");
}
// If already included or response already committed, perform include, else forward.
if (useInclude(request, response)) {
response.setContentType(getContentType());
if (logger.isDebugEnabled()) {
logger.debug("Including [" + getUrl() + "]");
}
rd.include(request, response);
}
else {
// Note: The forwarded resource is supposed to determine the content type itself.
if (logger.isDebugEnabled()) {
logger.debug("Forwarding to [" + getUrl() + "]");
}
rd.forward(request, response);
}
}
自定义参数绑定
遍历我们的参数解析器
//进行判断, 条件一,带有modelAttribute注解,或者条件二 不是一个简单类型
public boolean supportsParameter(MethodParameter parameter) {
return (parameter.hasParameterAnnotation(ModelAttribute.class) ||
(this.annotationNotRequired && !BeanUtils.isSimpleProperty(parameter.getParameterType())));
}
最终使用解析器:ServletModelAttributeMethodProcessor
核心代码
//创建一个空的我们的参数类型,属性为null
==attribute = createAttribute(name, parameter, binderFactory, webRequest);
//web数据绑定器,将请求参数绑定到指定的javaBean里面
//利用里面的Converters 将请求数据转成指定的数据类型,再次封装到javaBean中
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
//核心绑定方法:将我们的参数绑定到我们的javaBean 当中
bindRequestParameters(binder, webRequest);
@Override
@Nullable
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
Assert.state(mavContainer != null, "ModelAttributeMethodProcessor requires ModelAndViewContainer");
Assert.state(binderFactory != null, "ModelAttributeMethodProcessor requires WebDataBinderFactory");
String name = ModelFactory.getNameForParameter(parameter);
ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
if (ann != null) {
mavContainer.setBinding(name, ann.binding());
}
Object attribute = null;
BindingResult bindingResult = null;
if (mavContainer.containsAttribute(name)) {
attribute = mavContainer.getModel().get(name);
}
else {
// Create attribute instance
try {
//创建空的javabean
attribute = createAttribute(name, parameter, binderFactory, webRequest);
}
catch (BindException ex) {
if (isBindExceptionRequired(parameter)) {
// No BindingResult parameter -> fail with BindException
throw ex;
}
// Otherwise, expose null/empty value and associated BindingResult
if (parameter.getParameterType() == Optional.class) {
attribute = Optional.empty();
}
else {
attribute = ex.getTarget();
}
bindingResult = ex.getBindingResult();
}
}
if (bindingResult == null) {
// Bean property binding and validation;
// skipped in case of binding failure on construction.
// Web 数据绑定器 ,利用convers将数据进行转换,对bean中的属性进行绑定
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
if (binder.getTarget() != null) {
if (!mavContainer.isBindingDisabled(name)) {
//此方法就是将我们的传bing递的参数进行绑定到我们的javaBean中 ,主要进行绑定方法
bindRequestParameters(binder, webRequest);
}
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new BindException(binder.getBindingResult());
}
}
// Value type adaptation, also covering java.util.Optional
if (!parameter.getParameterType().isInstance(attribute)) {
attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
}
bindingResult = binder.getBindingResult();
}
// Add resolved attribute and BindingResult at the end of the model
Map<String, Object> bindingResultModel = bindingResult.getModel();
mavContainer.removeAttributes(bindingResultModel);
mavContainer.addAllAttributes(bindingResultModel);
return attribute;
}
//核心代码:设置我们的值
主要还是外面的bindRequestParameters(binder, webRequest);方法哇
//思路:先拿到我们的参数中的所有的键值对,一个一个进行判断选择我们的参数转换器
//拿到所有键值对
List<PropertyValue> propertyValues = pvs instanceof MutablePropertyValues ? ((MutablePropertyValues)pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues());
//找到converIfNecessary方法进行判断使用哪一个参数转换器,进行设置值
valueToApply = this.convertForProperty(tokens.canonicalName, oldValue, originalValue, ph.toTypeDescriptor());
//找到转换器
converIfNecessary()