处理大致流程图
getDataBinderFactory
其实就是先获取方法所在的类,看缓存里有没有相关方法,没有就找出有InitBinder
注解的方法放入缓存,然后再处理全局的绑定增强方法,有的话也要加入绑定集合,最后将绑定方法集合封装成WebDataBinderFactory
返回。
private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
Class<?> handlerType = handlerMethod.getBeanType();//方法的处理器类型
Set<Method> methods = this.initBinderCache.get(handlerType);//缓存里获取
if (methods == null) {//找出处理器类中有InitBinder注解的方法
methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
this.initBinderCache.put(handlerType, methods);//放入缓存
}
List<InvocableHandlerMethod> initBinderMethods = new ArrayList<>();//处理器绑定的方法
// Global methods first 全局的优先处理,也就是初始化的时候放进容器的@ControllerAdvice注解的类排在最前面
this.initBinderAdviceCache.forEach((controllerAdviceBean, methodSet) -> {
if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {
Object bean = controllerAdviceBean.resolveBean();
for (Method method : methodSet) {
initBinderMethods.add(createInitBinderMethod(bean, method));
}
}
});
for (Method method : methods) {//处理器的方法
Object bean = handlerMethod.getBean();
initBinderMethods.add(createInitBinderMethod(bean, method));//封装成InvocableHandlerMethod放入集合
}
return createDataBinderFactory(initBinderMethods);//封装成工厂返回
}
MethodIntrospector的selectMethods根据条件获取相应方法
他会获取targetType
以及父类和接口的所有的方法,然后找出有InitBinder
注解的方法,这个前面也有讲过,获取uri
映射初始化的地方。
public static Set<Method> selectMethods(Class<?> targetType, final ReflectionUtils.MethodFilter methodFilter) {
return selectMethods(targetType,
(MetadataLookup<Boolean>) method -> (methodFilter.matches(method) ? Boolean.TRUE : null)).keySet();
}
//条件就是方法上有InitBinder注解
public static final MethodFilter INIT_BINDER_METHODS = method ->
AnnotatedElementUtils.hasAnnotation(method, InitBinder.class);
createInitBinderMethod
创建可调用方法,其实就是封装了一层,设置了一些参数,以便于后面好操作。
private InvocableHandlerMethod createInitBinderMethod(Object bean, Method method) {
InvocableHandlerMethod binderMethod = new InvocableHandlerMethod(bean, method);
if (this.initBinderArgumentResolvers != null) {//设置参数解析器
binderMethod.setHandlerMethodArgumentResolvers(this.initBinderArgumentResolvers);
}
binderMethod.setDataBinderFactory(new DefaultDataBinderFactory(this.webBindingInitializer));
binderMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);//设置属性探测器
return binderMethod;
}
getModelFactory获取模型工厂
模型工厂跟数据相关,当然需要数据绑定帮忙啦,所以这里把处理方法和数据绑定工厂一起传进来了。
首先获取SessionAttributesHandler
,如果处理器类上有SessionAttributes
注解的话,就要获取相关信息。然后从缓存里获取有ModelAttribute
和RequestMapping
注解的方法,暂时叫他模型方法吧,这个方法会在处理方法前调用,可以理解为对模型做预处理。缓存不存在的话就去找出来放进集合,还是用MethodIntrospector.selectMethods
,条件就是方法有ModelAttribute
和RequestMapping
注解。然后将符合条件的方法放入缓存。然后再去处理全局的那种加入集合,最后将集合里的方法封装成InvocableHandlerMethod
,这个跟上面数据绑定的方法一样,最后创建一个模型工厂返回。
private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
Class<?> handlerType = handlerMethod.getBeanType();
Set<Method> methods = this.modelAttributeCache.get(handlerType);//缓存里取
if (methods == null) {//查找同时有RequestMapping和ModelAttribute注解的方法
methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
this.modelAttributeCache.put(handlerType, methods);
}
List<InvocableHandlerMethod> attrMethods = new ArrayList<>();
// Global methods first 处理全局的,排在最前面
this.modelAttributeAdviceCache.forEach((controllerAdviceBean, methodSet) -> {
if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {
Object bean = controllerAdviceBean.resolveBean();
for (Method method : methodSet) {
attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
}
}
});
for (Method method : methods) {
Object bean = handlerMethod.getBean();
attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
}
return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
}
模型方法条件
public static final MethodFilter MODEL_ATTRIBUTE_METHODS = method ->
(!AnnotatedElementUtils.hasAnnotation(method, RequestMapping.class) &&
AnnotatedElementUtils.hasAnnotation(method, ModelAttribute.class));
全局的模型方法
其实就是设置全局的属性:
先看下结果:
ModelFactory构造方法
把方法都封装成了ModelMethod
模型方法,其他的只是赋值。
public ModelFactory(@Nullable List<InvocableHandlerMethod> handlerMethods,
WebDataBinderFactory binderFactory, SessionAttributesHandler attributeHandler) {
if (handlerMethods != null) {
for (InvocableHandlerMethod handlerMethod : handlerMethods) {
this.modelMethods.add(new ModelMethod(handlerMethod));
}
}
this.dataBinderFactory = binderFactory;
this.sessionAttributesHandler = attributeHandler;
}
ModelMethod模型方法
这里会进行方法的参数检查,如果有ModelAttribute
注解的参数,那就是一定要的,所以会添加到依赖dependencies
中,依赖在后面调用模型方法的时候会有用,但是这里的依赖是字符串类型,所以要获取参数名字,如果在ModelAttribute
注解中没写的话,默认会是参数类型的首字母小写为名字,比如String
类型的话,就是string
。
public ModelMethod(InvocableHandlerMethod handlerMethod) {
this.handlerMethod = handlerMethod;
for (MethodParameter parameter : handlerMethod.getMethodParameters()) {
if (parameter.hasParameterAnnotation(ModelAttribute.class)) {
this.dependencies.add(getNameForParameter(parameter));
}
}
}
比如这样模型方法有参数@ModelAttribute String aaa
,ModelAttribute
注解内没指定名字,而方法只有参数类型信息:
他的依赖属性名就是:
至此我们数据绑定工厂和模型工厂创建好了,解析去就要做事了。
好了,今天就到这里了,希望对学习理解有帮助,大神看见勿喷,仅为自己的学习理解,能力有限,请多包涵。
Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。
它的内容包括:
- 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
- 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
- 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
- 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
- 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
- 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
- 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
- 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw
目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:
想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询
同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。