美文网首页
RequestMappingHandlerAdapter方法调用

RequestMappingHandlerAdapter方法调用

作者: 程序员札记 | 来源:发表于2023-06-19 09:26 被阅读0次

处理流程图

image.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));

全局的模型方法

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


image.png image.png image.png

先看下结果:


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

image.png

他的依赖属性名就是:

image.png

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

相关文章

网友评论

      本文标题:RequestMappingHandlerAdapter方法调用

      本文链接:https://www.haomeiwen.com/subject/wwqeydtx.html