2023-09-14  阅读(1)
原文作者:王伟王胖胖 原文地址: https://blog.csdn.net/wangwei19871103/article/details/105536867

处理大致流程图

202309142302026371.png

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注解的话,就要获取相关信息。然后从缓存里获取有ModelAttributeRequestMapping注解的方法,暂时叫他模型方法吧,这个方法会在处理方法前调用,可以理解为对模型做预处理。缓存不存在的话就去找出来放进集合,还是用MethodIntrospector.selectMethods,条件就是方法有ModelAttributeRequestMapping注解。然后将符合条件的方法放入缓存。然后再去处理全局的那种加入集合,最后将集合里的方法封装成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));

全局的模型方法

其实就是设置全局的属性:

202309142302064162.png

202309142302072643.png

202309142302097684.png
先看下结果:

202309142302105345.png

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 aaaModelAttribute注解内没指定名字,而方法只有参数类型信息:

202309142302114646.png
他的依赖属性名就是:

202309142302126797.png

至此我们数据绑定工厂和模型工厂创建好了,解析去就要做事了。

好了,今天就到这里了,希望对学习理解有帮助,大神看见勿喷,仅为自己的学习理解,能力有限,请多包涵。


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] ,回复【面试题】 即可免费领取。

阅读全文