SpringMVC之源码分析--HandlerMapping(四)

概述

本节我们继续分析HandlerMapping另一个实现类ReqeustMappingHandlerMapping,该类是我们日常开发中使用最多的映射器策略,即我们在开发中使用的注解开发方式,如:@Controller、@RequestMapping等,都使用的是此映射策略。Spring MVC默认支持该策略。

本系列文章是基于Spring5.0.5RELEASE。

类图

类的继承关系,如下图:

《SpringMVC之源码分析--HandlerMapping(四)》

第一次看到此图可能会感觉好复杂,大家别急,学技术就是这样,首先需要静下心,再一个要学会对已掌握知识点做总结、对比、分类,这样才能把所有的知识点串起来,能系统的了解一项技术。

以上是我个人的一些经验,希望对别人能有帮助,废话不多说了,我们来分析下这张图,之前我们学过了SimpleUrlHandlerMapping和BeanNameUrlHandlerMapping,通过横向对比,我们发现,他们都继承自AbstractHandlerMapping抽象类,而AbstractHandlerMapping类的主要工作就是初始化拦截器的功能,三者的实现都是一样的。

继续分析,我们发现RequestMappingHandlerMapping增加实现了InitializingBean和EmbeddedVualeResolverAware接口,即增加了如下能力:

  • 实现InitializingBean接口,增加了bean初始化的能力,也就是说在bean初始化时可以做一些控制
  • 实现EmbeddedValueResolverAware接口,即增加了读取属性文件的能力

这两个接口都是Spring自动帮我们调用其中的方法的。也就是说,RequestMappingHandlerMapping通过这些能力来完成初始化HandlerMapping接口的。

以上是对RequestMappingHandlerMapping的宏观分析,下面我们进行内部细节分析。

整体流程

一、通过实现ApplicationContextAware接口,完成拦截器相关组件的初始化

  • 调用AbstractHandlerMapping类的initApplicationContext()方法。//TODO 下节分析

二、通过实现InitializingBean接口,完成url与处理器方法的映射(url->RequestMappingInfo,RequstMappingInfo->HandlerMehtod)

  • 调用RequestMappingHandlerMapping类实现的afterPropertiesSet()方法,通过该方法最终调到其父类的initHandlerMethods()方法,这个方法是完成映射解析工作:

    1、获取上下文环境中所有的bean

    2、迭代所有的bean,通过isHandler方法判断是否是handler

    2.1 调用RequestMappingHandlerMapping.isHandler方法,根据@Controller或@RequestMapping注解判断(“或”关系,任意一个)

    3、解析出handler中所有需要处理的方法,即标注了@RequestMapping注解的方法,封装在detectHandlerMehtods方法中

    3.1 获取到原始的Class<?>

    3.2 使用MethodIntrospector.selectMethods方法过滤具体的handler method,通过模板方法模式getMappingForMethod预留给子类

    • RequestMappingHandlerMapping.getMappingForMehtod方法,通过方法、类上面@RequestMapping注解生成匹配条件RequestMappingInfo对象

    3.3 对过滤到的每个method进行注册,通过registerHandlerMehtod方法完成

    • 通过createHandlerMethod方法创建HandlerMethod对象来封装处理器方法
    • 判断匹配条件与处理器是否冲突,即同一个匹配条件只能对应一个处理器方法
    • 把匹配条件与处理器方法存入map
    • 从匹配条件中解析出url,通过RequestMappingInfoHandlerMapping.getMappingPathPatterns方法实现,之后把url与匹配条件存入另一个map

    4、对HandlerMethod进行初始化,调用handlerMethodsInitialized方法,其内部什么都没做

以上是初始化RequestMappingHandlerMapping的整体流程。

源码分析

  • AbstractHandlerMapping

初始化拦截器


// 初始化拦截器,即初始化url需要的拦截器
@Override
protected void initApplicationContext() throws BeansException {
    extendInterceptors(this.interceptors);
    detectMappedInterceptors(this.adaptedInterceptors);
    initInterceptors();
}
  • AbstractHandlerMethodMapping

控制初始化RequestMappingHandlerMapping.initHanderMethods方法整体流程,代码如下:

/**
 * 在ApplicationContext上下文扫描所有的bean,检测和注册处理器方法(handler method)
 */
protected void initHandlerMethods() {
    if (logger.isDebugEnabled()) {
        logger.debug("Looking for request mappings in application context: " + getApplicationContext());
    }
    // 从上下文中查找所有的bean
    String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
            BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
            obtainApplicationContext().getBeanNamesForType(Object.class));
    
    // 遍历所有的beanName
    for (String beanName : beanNames) {
        if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
            Class<?> beanType = null;
            try {
                // 获得bean的类型
                beanType = obtainApplicationContext().getType(beanName);
            }
            catch (Throwable ex) {
                // An unresolvable bean type, probably from a lazy bean - let's ignore it.
                if (logger.isDebugEnabled()) {
                    logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
                }
            }
            // 判断类是否被@Controller或是@RequestMapping注释
            // isHandler方法由子类RequestMappingHandlerMapping去实现
            if (beanType != null && isHandler(beanType)) {
                // 注册url与处理器方法的关系
                detectHandlerMethods(beanName);
            }
        }
    }
    // 空方法
    handlerMethodsInitialized(getHandlerMethods());
}

/**
 * 查找处理程序handler中的处理方法
 */
protected void detectHandlerMethods(final Object handler) {
    // 获取handler的类型
    Class<?> handlerType = (handler instanceof String ?
            obtainApplicationContext().getType((String) handler) : handler.getClass());

    if (handlerType != null) {
        // 获取给定类的用户定义类型,通常为给定类的类型,但在cglib生成子类的情况下,返回的是原始类型
        final Class<?> userType = ClassUtils.getUserClass(handlerType);
        // 获取处理器方法map,key是Method,value是匹配条件RequestMappingInfo对象
        // map中不包括未被@RequestMapping注解的方法
        Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
                (MethodIntrospector.MetadataLookup<T>) method -> {
                    try {
                        // 调用子类RequestMappingHandlerMapping的getMappingForMethod方法进行处理,即根据RequestMapping注解信息创建匹配条件RequestMappingInfo对象
                        return getMappingForMethod(method, userType);
                    }
                    catch (Throwable ex) {
                        throw new IllegalStateException("Invalid mapping on handler class [" +
                                userType.getName() + "]: " + method, ex);
                    }
                });
        if (logger.isDebugEnabled()) {
            logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
        }
        // 遍历map,获得Method和RequestMappingInfo,注册他们
        methods.forEach((method, mapping) -> {
            Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
            // 调用本类registerHandlerMethod()方法
            registerHandlerMethod(handler, invocableMethod, mapping);
        });
    }
}

/**
 * 注册rul和处理器方法的映射关系
 */
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
    // 调用内部类MappingRegistry的register()方法
    this.mappingRegistry.register(mapping, handler, method);
}

/**
 * 此方法是内部类MappingRegistry的方法
 */
public void register(T mapping, Object handler, Method method) {
    this.readWriteLock.writeLock().lock();
    try {
        // 创建处理器方法HandlerMethod实例,即Controller中的处理方法
        HandlerMethod handlerMethod = createHandlerMethod(handler, method);
        // 判断匹配条件是否重复,即一个@RequestMapping的映射url只能对应一个方法
        assertUniqueMethodMapping(handlerMethod, mapping);

        if (logger.isInfoEnabled()) {
            logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod);
        }
        // 将匹配条件RequestMappingInfo和处理器方法保存到map中
        this.mappingLookup.put(mapping, handlerMethod);

        // 获得url映射路径,将映射路径和匹配条件对象RequestMappingInfo存起来
        // 调用本类的getDerectUrls方法
        List<String> directUrls = getDirectUrls(mapping);
        for (String url : directUrls) {
            this.urlLookup.add(url, mapping);
        }

        String name = null;
        if (getNamingStrategy() != null) {
            name = getNamingStrategy().getName(handlerMethod, mapping);
            addMappingName(name, handlerMethod);
        }

        CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
        if (corsConfig != null) {
            this.corsLookup.put(handlerMethod, corsConfig);
        }
        
        // 将映射注册对象存入map
        this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
    }
    finally {
        this.readWriteLock.writeLock().unlock();
    }
}

private List<String> getDirectUrls(T mapping) {
    List<String> urls = new ArrayList<>(1);
    // 调用子类RequestMappingInfoHandlerMapping.getMappingPathPatterns方法
    for (String path : getMappingPathPatterns(mapping)) {
        if (!getPathMatcher().isPattern(path)) {
            urls.add(path);
        }
    }
    return urls;
}
  • RequestMappingHandlerMapping

根据@RequestMapping生成RequestMappingInfo对象,主要代码如下:

/**
 * 使用方法和类型注解@RequestMapping创建RequestMappingInfo对象
 */
@Override
@Nullable
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
    // 创建方法的RequestMappingInfo
    RequestMappingInfo info = createRequestMappingInfo(method);
    if (info != null) {
        // 创建类的RequestMappingInfo
        RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
        if (typeInfo != null) {
            // 将方法RequestMappingInfo和类RequestMappingInfo合并,比如Controller类上有@RequestMapping("/demo"),方法的@RequestMapping("/demo1"),结果为"/demo/demo1"
            info = typeInfo.combine(info);
        }
    }
    return info;
}

@Nullable
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
    // 获取RequestMapping注解
    RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
    RequestCondition<?> condition = (element instanceof Class ?
            getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
    // 调用createRequestMappingInfo创建匹配条件对象
    return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}

/**
 * 构造匹配条件对象
 */
protected RequestMappingInfo createRequestMappingInfo(
        RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {

    RequestMappingInfo.Builder builder = RequestMappingInfo
            .paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
            .methods(requestMapping.method())
            .params(requestMapping.params())
            .headers(requestMapping.headers())
            .consumes(requestMapping.consumes())
            .produces(requestMapping.produces())
            .mappingName(requestMapping.name());
    if (customCondition != null) {
        builder.customCondition(customCondition);
    }
    return builder.options(this.config).build();
}
  • RequestMappingInfoHandlerMapping

提供匹配条件RequestMappingInfo的解析处理,涉及的代码如下:

/**
 * 获取url集合,即@RequestMapping中设置的value或path
 */
@Override
protected Set<String> getMappingPathPatterns(RequestMappingInfo info) {
    return info.getPatternsCondition().getPatterns();
}

以上即RequestMappingHandlerMapping对象的初始化过程及初始化过程的核心源码。

总结

本文主要分析了RequestMappingHandlerMapping的初始化过程,希望对大家有帮助。随着学习的深入,后面有时间在分析下期中涉及的关键bean,比如:RequestMappingInfo、RequestCondition、HandlerMethod等等。大家加油!

最后创建了qq群方便大家交流,可扫描加入,同时也可加我qq:276420284,共同学习、共同进步,谢谢!

《SpringMVC之源码分析--HandlerMapping(四)》

    原文作者:Spring MVC
    原文地址: https://segmentfault.com/a/1190000015009343
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞