@InitBinder and ModelAttributeMethodProcessor 处理 @ModelAttribute 和兜底无注解
参考:springmvc--8-ServletModelAttributeMethodProcessor兜底处理@ModelAttribute注解和无注解
继承关系比较简单,两个接口处理 Controller 参数和返回值的,ModelAttributeMethodProcessor 本身也不是一个抽象类。
构造函数和字段
annotationNotRequired 为 true 则支持不使用 @ModelAttribut 参数时的非简单参数/返回值的处理
public class ServletModelAttributeMethodProcessor extends ModelAttributeMethodProcessor {
public ServletModelAttributeMethodProcessor(boolean annotationNotRequired) {
super(annotationNotRequired);
}
}
public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {
private final boolean annotationNotRequired;
public ModelAttributeMethodProcessor(boolean annotationNotRequired) {
this.annotationNotRequired = annotationNotRequired;
}
}
支持的参数和返回值
使用 ModelAttribute 注解 or annotationNotRequired 为 true 时的非简单类型(BeanUtils 判断,不仅仅是基本数据类型)
public boolean supportsParameter(MethodParameter parameter) {
return (parameter.hasParameterAnnotation(ModelAttribute.class) ||
(this.annotationNotRequired && !BeanUtils.isSimpleProperty(parameter.getParameterType())));
}
public boolean supportsReturnType(MethodParameter returnType) {
return (returnType.hasMethodAnnotation(ModelAttribute.class) ||
(this.annotationNotRequired && !BeanUtils.isSimpleProperty(returnType.getParameterType())));
}
处理返回值
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
if (returnValue != null) {
// name 大概是处理成 com.example.Demo ---> demo 这样
String name = ModelFactory.getNameForReturnValue(returnValue, returnType);
// 设置 model, k-v
mavContainer.addAttribute(name, returnValue);
}
}
处理请求参数
调用栈
resolveArgument:129, ModelAttributeMethodProcessor
resolveArgument:122, HandlerMethodArgumentResolverComposite
getMethodArgumentValues:179, InvocableHandlerMethod
invokeForRequest:146, InvocableHandlerMethod
invokeAndHandle:117, ServletInvocableHandlerMethod
invokeHandlerMethod:895, RequestMappingHandlerAdapter
handleInternal:808, RequestMappingHandlerAdapter
handle:87, AbstractHandlerMethodAdapter
doDispatch:1067, DispatcherServlet
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
// 参数名称
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 {
// 没有则创建
attribute = createAttribute(name, parameter, binderFactory, webRequest);
}
if (bindingResult == null) {
// 绑定器
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
// 上面的 attribute 就是 target
if (binder.getTarget() != null) {
// 没有被禁, 则绑定
if (!mavContainer.isBindingDisabled(name)) {
bindRequestParameters(binder, webRequest);
}
// 绑定后的 valid 校验
validateIfApplicable(binder, parameter);
// 有错误是否抛出绑定错误
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new BindException(binder.getBindingResult());
}
}
// 尝试进行类型转换, attribute 是否为此类的实例
if (!parameter.getParameterType().isInstance(attribute)) {
attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
}
bindingResult = binder.getBindingResult();
}
// 绑定结果
Map<String, Object> bindingResultModel = bindingResult.getModel();
mavContainer.removeAttributes(bindingResultModel);
mavContainer.addAllAttributes(bindingResultModel);
return attribute;
}
createAttribute
// 在 ModelAttributeMethodProcessor 就是尝试创建实例, 有构造函数参数则尝试从请求参数中获取
// ServletModelAttributeMethodProcessor
// - 先尝试从 UriTemplateVariables 即 @PathVariable 中找值, 存在则尝试创建实例(并转换类型)
// - 不行则调用 ModelAttributeMethodProcessor 的
WebDataBinderFactory 和 WebDataBinder
WebDataBinderFactory
在这创建:RequestMappingHandlerAdapter#invokeHandlerMethod
创建时依赖 HandlerMethod。
private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
// Controller(或被代理过的)
Class<?> handlerType = handlerMethod.getBeanType();
// @InitBinder 注解的方法, 这里是缓存
Set<Method> methods = this.initBinderCache.get(handlerType);
// 没有则尝试找, 找过的没有会是 empty, 【MethodIntrospector】
if (methods == null) {
methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
this.initBinderCache.put(handlerType, methods);
}
List<InvocableHandlerMethod> initBinderMethods = new ArrayList<>();
// Global methods first
// ControllerAdvice 的 @InitBinder
this.initBinderAdviceCache.forEach((controllerAdviceBean, methodSet) -> {
if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {
Object bean = controllerAdviceBean.resolveBean();
for (Method method : methodSet) {
initBinderMethods.add(createInitBinderMethod(bean, method));
}
}
});
// createInitBinderMethod 生成 InvocableHandlerMethod, 封装后感觉有点和 HandlerMethod 一样了, 方法参数也是可以在请求参数里查找的
for (Method method : methods) {
Object bean = handlerMethod.getBean();
initBinderMethods.add(createInitBinderMethod(bean, method));
}
return createDataBinderFactory(initBinderMethods);
}
private InvocableHandlerMethod createInitBinderMethod(Object bean, Method method) {
InvocableHandlerMethod binderMethod = new InvocableHandlerMethod(bean, method);
// 每个 InvocableHandlerMethod 都能得所有得参数解析器
if (this.initBinderArgumentResolvers != null) {
binderMethod.setHandlerMethodArgumentResolvers(this.initBinderArgumentResolvers);
}
// 数据绑定器 & 绑定器得初始化(含转换器、校验器等)
binderMethod.setDataBinderFactory(new DefaultDataBinderFactory(this.webBindingInitializer));
// 参数名称解析器
binderMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
return binderMethod;
}
protected InitBinderDataBinderFactory createDataBinderFactory(List<InvocableHandlerMethod> binderMethods)
throws Exception {
return new ServletRequestDataBinderFactory(binderMethods, getWebBindingInitializer());
}
WebDataBinder的创建
DefaultDataBinderFactory#createBinder
public final WebDataBinder createBinder(
NativeWebRequest webRequest, @Nullable Object target, String objectName) throws Exception {
// 目标实例, 实例名称, 请求
WebDataBinder dataBinder = createBinderInstance(target, objectName, webRequest);
// factory 创建时得到的各种 Conversion、validator 等
if (this.initializer != null) {
this.initializer.initBinder(dataBinder, webRequest);
}
// @InitBinder 调用
initBinder(dataBinder, webRequest);
return dataBinder;
}
public void initBinder(WebDataBinder dataBinder, NativeWebRequest request) throws Exception {
for (InvocableHandlerMethod binderMethod : this.binderMethods) {
if (isBinderMethodApplicable(binderMethod, dataBinder)) {
// 不能有返回值
// 传了 WebDataBinder 可以向其中加一些东西, 比如
Object returnValue = binderMethod.invokeForRequest(request, null, dataBinder);
if (returnValue != null) {
throw new IllegalStateException(
"@InitBinder methods must not return a value (should be void): " + binderMethod);
}
}
}
}
@InitBinder
public void initBinder(WebDataBinder binder){
// Date 类型转换
binder.registerCustomEditor(Date.class, new PropertyEditorSupport(){
@Override
public void setAsText(String text){
setValue(DateUtils.parseDate(text));
}
});
}
ModelFactory 的创建
同样是在这里 RequestMappingHandlerAdapter#invokeHandlerMethod
private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
// @SessionAttributes
SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
Class<?> handlerType = handlerMethod.getBeanType();
Set<Method> methods = this.modelAttributeCache.get(handlerType);
// 没有 @RequestMapping 但是有 @ModelAttribute 的方法, 这种方法在每个实际 Controller 请求之前会被调用此役
if (methods == null) {
methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
this.modelAttributeCache.put(handlerType, methods);
}
List<InvocableHandlerMethod> attrMethods = new ArrayList<>();
// Global methods first
// 存粹的 ControllerAdvice 方法 ?
this.modelAttributeAdviceCache.forEach((controllerAdviceBean, methodSet) -> {
if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {
Object bean = controllerAdviceBean.resolveBean();
for (Method method : methodSet) {
attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
}
}
});
// @ModelAttribute 方法
for (Method method : methods) {
Object bean = handlerMethod.getBean();
attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
}
// 按顺序应该是先 ControllerAdvice ? 然后 @ModelAttribute
// ModelFactory 里面会拆分出依赖的 @ModelAttribute(需要在 Controller 方法执行之前执行)
return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
}
大概看下外层的构建
RequestMappingHandlerAdapter#invokeHandlerMethod
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
// 封装一下
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
// DataBinder, 处理了 @InitBinder
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
// 处理了 @ModelAttribute
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
// 再封装一层
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
// 参数解析
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
// 返回值处理
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
//
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
// ModelAndViewContainer
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
// @ModelAttribute 方法在内部已经被调用了
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
// 调用
invocableMethod.invokeAndHandle(webRequest, mavContainer);
// ModelAndView
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
// 请求处理结束
webRequest.requestCompleted();
}
}
调用概略
RequestMappingHandlerAdapter#invokeHandlerMethod
-> ServletInvocableHandlerMethod#invokeAndHandle
-> InvocableHandlerMethod#invokeForRequest
-> InvocableHandlerMethod#getMethodArgumentValues
-> 进行参数解析
-> InvocableHandlerMethod#doInvoke
-> 方法调用
ServletInvocableHandlerMethod
说是封装 HandlerMethod,实际上封装了:@InitBinder、@ModelAttribute、ExceptionResolver