SpringMVC源码解析(二)——HandlerMapping

前言

    上一篇涉及的许多例如容器实例化、容器刷新等,在之前Spring源码解析中都已讲解过,不再赘述。本节我们来分析下 HandlerMapping 的初始化,这里的初始化工作将对我们之后的请求映射起到关键作用。

 

源码解读

    我们首先来看下接口 HandlerMapping ,接口只定义一个方法,通过 request 请求返回一个 HandlerExecutionChain 对象。

public interface HandlerMapping {

    HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

    接着来看下 HandlerExecutionChain 里面有哪些属性。

public class HandlerExecutionChain {
    // 处理器对象
    private final Object handler;
    // 拦截器数组
    private HandlerInterceptor[] interceptors;

    public Object getHandler() {
        return this.handler;
    }
    
    // 遍历执行拦截器
    boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HandlerInterceptor[] interceptors = getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            for (int i = 0; i < interceptors.length; i++) {
                HandlerInterceptor interceptor = interceptors[i];
                if (!interceptor.preHandle(request, response, this.handler)) {
                    triggerAfterCompletion(request, response, null);
                    return false;
                }
                this.interceptorIndex = i;
            }
        }
        return true;
    }
}

    这里以 applyPreHandle 为例,还有 applyPostHandletriggerAfterCompletion,分别遍历调用拦截器的 postHandle 和 afterCompletion。

    我们大致可以分析出,HandlerMapping 就是根据请求 request,获取到对应的请求执行链。准备知识过后,让我们来看看 HandlerMapping 的初始化逻辑:

public class DispatcherServlet extends FrameworkServlet {
    
    // 存放加载的 HandlerMapping实现
    private List<HandlerMapping> handlerMappings;

    private boolean detectAllHandlerMappings = true;

    // 衔接上一节
    @Override
    protected void onRefresh(ApplicationContext context) {
        initStrategies(context);
    }

    protected void initStrategies(ApplicationContext context) {
        ......
        // 关注该方法:HandlerMapping 的初始化
        initHandlerMappings(context);
        ......
    }

    private void initHandlerMappings(ApplicationContext context) {
        this.handlerMappings = null;

        // 可指定,默认为 true(即探测所有实现了HandlerMapping接口的 bean)
        if (this.detectAllHandlerMappings) {
            // beansOfTypeIncludingAncestors:通过 getBeansOfType获取子容器和父容器内的 HandlerMapping
            // getBeansOfType会首先调用 getBeanNamesForType获取指定类型的所有 beanName
            // 然后遍历这些 beanName,使用 getBean创建实例
            Map<String, HandlerMapping> matchingBeans =
                    BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
            if (!matchingBeans.isEmpty()) {
                this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
                // 对于实现了 PriorityOrdered、Ordered的 HandleMapping 排序
                // 如果没有实现这俩接口,默认优先级最低
                AnnotationAwareOrderComparator.sort(this.handlerMappings);
            }
        } else {
            try {
                // 获取 beanName为“handlerMapping”的 HandlerMapping
                HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
                this.handlerMappings = Collections.singletonList(hm);
            } catch (NoSuchBeanDefinitionException ex) {
                // Ignore, we'll add a default HandlerMapping later.
            }
        }

        // 如果上述处理后未找到 HandlerMapping
        if (this.handlerMappings == null) {
            // 默认策略:见 DispatcherServlet.properties(存储了缺失的默认初始化类型)
            // 会注册 BeanNameUrlHandlerMapping和 DefaultAnnotationHandlerMapping
            this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
            if (logger.isDebugEnabled()) {
                logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
            }
        }
    }

    protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
        // 按传入 HandlerMapping举例,key = org.springframework.web.servlet.HandlerMapping
        String key = strategyInterface.getName();
        // 获取对应的值:org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,
        // org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
        String value = defaultStrategies.getProperty(key);
        if (value != null) {
            // 根据逗号分割
            String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
            List<T> strategies = new ArrayList<T>(classNames.length);
            for (String className : classNames) {
                try {
                    Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
                    // 调用 createBean创建该实例
                    Object strategy = createDefaultStrategy(context, clazz);
                    strategies.add((T) strategy);
                } ...// 省略 catch
            }
            return strategies;
        } else {
            return new LinkedList<T>();
        }
    }
}

    这一步的初始化,默认的逻辑就将探测到所有 HandleMapping 类型的实例,赋值给成员变量 handlerMappings。看似好像没有很复杂的逻辑,其实背后隐藏了很多细节。

    这里我们首先要介绍一个类,ApplicationObjectSupport

public abstract class ApplicationObjectSupport implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    public final void setApplicationContext(ApplicationContext context) throws BeansException {
        if (context == null && !isContextRequired()) {
            this.applicationContext = null;
            this.messageSourceAccessor = null;
        } else if (this.applicationContext == null) {
            // ApplicationContext类型检测
            if (!requiredContextClass().isInstance(context)) {
                throw new ApplicationContextException(
                        "Invalid application context: needs to be of type [" +
                                requiredContextClass().getName() + "]");
            }
            // 赋值,用于判断是否重复初始化(及父子容器初始化,该方法也只会执行一次)
            this.applicationContext = context;
            this.messageSourceAccessor = new MessageSourceAccessor(context);
            // 扩展点
            initApplicationContext(context);
        } else {
            // Ignore reinitialization if same context passed in.
            if (this.applicationContext != context) {
                throw new ApplicationContextException(
                        "Cannot reinitialize with different application context: current one is ["
                                + this.applicationContext + "], passed-in one is [" + context + "]");
            }
        }
    }

    protected void initApplicationContext(ApplicationContext context) throws BeansException {
        initApplicationContext();
    }

    /**
     * 被子类扩展以实现自定义的初始化逻辑
     */
    protected void initApplicationContext() throws BeansException {
    }
}

    我们来分析一下 setApplicationContext 调用时机,首先该方法来自 ApplicationContextAware,该方法是在 ApplicationContextAwareProcessor.invokeAwareInterfaces 被调用。

class ApplicationContextAwareProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
        .....// 省略 AccessControlContext的获取
        if (acc != null) {
            AccessController.doPrivileged(new PrivilegedAction<Object>() {
                @Override
                public Object run() {
                    invokeAwareInterfaces(bean);
                    return null;
                }
            }, acc);
        } else {
            invokeAwareInterfaces(bean);
        }
        return bean;
    }

    private void invokeAwareInterfaces(Object bean) {
        if (bean instanceof Aware) {
            .....// 省却其他一些 Aware的调用
            if (bean instanceof ApplicationContextAware) {
                ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
            }
        }
    }
}

    省略了一些代码,可以看出是在 Bean 的生命周期接口方法 BeanPostProcessor.postProcessBeforeInitialization 被调用的。

    而 ApplicationContextAwareProcessor 的注册就要追溯到“ 容器刷新 ”的 prepareBeanFactory 方法了。

    绕了一圈,我们发现,其实 Spring 的实现隐藏在了许多细节里,这样的实现让代码的耦合程度非常低,极易将逻辑封装到各个模块中。

    我们再回到之前提到的 ApplicationObjectSupport ,这个类提供了 initApplicationContext 可供子类实现定制化的逻辑。

 

AbstractHandlerMapping

// WebApplicationObjectSupport为 ApplicationObjectSupport子类
public abstract class AbstractHandlerMapping 
                   extends WebApplicationObjectSupport implements HandlerMapping, Ordered {

    // 这一步主要是拦截器的相关处理
    @Override
    protected void initApplicationContext() throws BeansException {
        // 空实现
        extendInterceptors(this.interceptors);
        // 探测所有 MappedInterceptor填充 adaptedInterceptors
        // <mvc:interceptors>下的一个个 <mvc:interceptor>就会被封装成 MappedInterceptor
        detectMappedInterceptors(this.adaptedInterceptors);

        initInterceptors();
    }

    protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
        mappedInterceptors.addAll(
                BeanFactoryUtils.beansOfTypeIncludingAncestors(
                        getApplicationContext(), MappedInterceptor.class, true, false).values());
    }
}

    AbstractHandlerMapping 作为 HandlerMapping 类型的顶层抽象基类,自身实现了拦截器的初始化工作。子类划分两大分支:AbstractUrlHandlerMapping 和 AbstractHandlerMethodMapping

 

AbstractUrlHandlerMapping 分支

// AbstractUrlHandlerMapping为 AbstractHandlerMapping子类
public abstract class AbstractDetectingUrlHandlerMapping extends AbstractUrlHandlerMapping {

    // 默认不从父容器查找“请求处理器”
    private boolean detectHandlersInAncestorContexts = false;

    @Override
    public void initApplicationContext() throws ApplicationContextException {
        super.initApplicationContext();
        // 探测 Handler的逻辑
        detectHandlers();
    }

    protected void detectHandlers() throws BeansException {
        if (logger.isDebugEnabled()) {
            logger.debug("Looking for URL mappings in application context: " + getApplicationContext());
        }
        // 默认只在子容器中查找,首先拿到所有注册的 beanName
        String[] beanNames = (this.detectHandlersInAncestorContexts ?
           BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) : getApplicationContext().getBeanNamesForType(Object.class));

        for (String beanName : beanNames) {
            // 会将能作为 Handler实例对应的 urls返回
            String[] urls = determineUrlsForHandler(beanName);
            if (!ObjectUtils.isEmpty(urls)) {
                // 注册 urls和 Handler的映射关系
                registerHandler(urls, beanName);
            } else {
                if (logger.isDebugEnabled()) {
                    logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");
                }
            }
        }
    }
}

    AbstractDetectingUrlHandlerMapping 以模板方法,将挑选请求处理器以抽象方法 determineUrlsForHandler 留给子类实现不同策略,自身通过调用父类 registerHandler 实现了处理器的注册逻辑。

public abstract class AbstractUrlHandlerMapping 
                        extends AbstractHandlerMapping implements MatchableHandlerMapping {

    protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException {
        Assert.notNull(urlPaths, "URL path array must not be null");
        for (String urlPath : urlPaths) {
            registerHandler(urlPath, beanName);
        }
    }

    protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
        Assert.notNull(urlPath, "URL path must not be null");
        Assert.notNull(handler, "Handler object must not be null");
        Object resolvedHandler = handler;

        // 将 beanName通过 getBean转换成单例对象
        if (!this.lazyInitHandlers && handler instanceof String) {
            String handlerName = (String) handler;
            if (getApplicationContext().isSingleton(handlerName)) {
                resolvedHandler = getApplicationContext().getBean(handlerName);
            }
        }

        // handlerMap维护 url和 Handler的关联关系
        Object mappedHandler = this.handlerMap.get(urlPath);
        // 确保一个 url仅能对应唯一的处理器
        if (mappedHandler != null) {
            if (mappedHandler != resolvedHandler) {
                ....// 抛异常,指明一个 url只能对应一个处理器
            }
        } else {
            if (urlPath.equals("/")) {
                if (logger.isInfoEnabled()) {
                    logger.info("Root mapping to " + getHandlerDescription(handler));
                }
                // 将 url为“/”的设置根处理器
                setRootHandler(resolvedHandler);
            } else if (urlPath.equals("/*")) {
                if (logger.isInfoEnabled()) {
                    logger.info("Default mapping to " + getHandlerDescription(handler));
                }
                // 将 url为“/*”的设置默认处理器
                setDefaultHandler(resolvedHandler);
            } else {
                // 其余的存储关联关系
                this.handlerMap.put(urlPath, resolvedHandler);
                if (logger.isInfoEnabled()) {
                    logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler));
                }
            }
        }
    }
}

    注册就是将 url 数组和对应的 Handler 关联起来。那么什么样的实例才能作为 Handler 呢?接下来我们看看不同子类实现 determineUrlsForHandler 是如何获取 Handler对应的 urls。

 

BeanNameUrlHandlerMapping
public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping {

    /**
     * beanName或 别名是以“/”开头的.
     */
    @Override
    protected String[] determineUrlsForHandler(String beanName) {
        List<String> urls = new ArrayList<String>();
        if (beanName.startsWith("/")) {
            urls.add(beanName);
        }
        String[] aliases = getApplicationContext().getAliases(beanName);
        for (String alias : aliases) {
            if (alias.startsWith("/")) {
                urls.add(alias);
            }
        }
        return StringUtils.toStringArray(urls);
    }
}

    这种最为简单,就是把 beanName、alias 以“/”开头的实例当作 Handler,然后 urls 就是这些实例的 beanName、alias(别名)。

 

DefaultAnnotationHandlerMapping
@Deprecated
public class DefaultAnnotationHandlerMapping extends AbstractDetectingUrlHandlerMapping {
    
    // 存放被 @RequestMapping标识的类,和注解的映射关系
    private final Map<Class<?>, RequestMapping> cachedMappings = new HashMap<Class<?>, RequestMapping>();


    /**
     * 这个方法会返回指定 beanName下组装好的所有 urls
     * 组装即类级别和方法级别 @RequestMapping value的组合
     * 如果类未被 @Controller或 @RequestMapping标识,返回 null
     */
    @Override
    protected String[] determineUrlsForHandler(String beanName) {
        ApplicationContext context = getApplicationContext();
        // 获取指定 beanName的 Class类型
        Class<?> handlerType = context.getType(beanName);
        // 判断该类是否被 @RequestMapping标识
        RequestMapping mapping = context.findAnnotationOnBean(beanName, RequestMapping.class);

        // 这里处理的是类级别的 @RequestMapping
        if (mapping != null) {
            this.cachedMappings.put(handlerType, mapping);
            Set<String> urls = new LinkedHashSet<String>();
            // 获取类级别 @RequestMapping的 value
            String[] typeLevelPatterns = mapping.value();
            if (typeLevelPatterns.length > 0) {
                // 这一步会返回所有方法级别 @RequestMapping的 value全集
                String[] methodLevelPatterns = determineUrlsForHandlerMethods(handlerType, true);
                for (String typeLevelPattern : typeLevelPatterns) {
                    // 如果定义的 url前没有“/”,自动添加
                    if (!typeLevelPattern.startsWith("/")) {
                        typeLevelPattern = "/" + typeLevelPattern;
                    }
                    boolean hasEmptyMethodLevelMappings = false;
                    for (String methodLevelPattern : methodLevelPatterns) {
                        if (methodLevelPattern == null) {
                            hasEmptyMethodLevelMappings = true;
                        } else {
                            // 将类指定 url和方法指定 url拼接
                            String combinedPattern = getPathMatcher().combine(typeLevelPattern, methodLevelPattern);
                            addUrlsForPath(urls, combinedPattern);
                        }
                    }
                    // 如果方法级别 @RequestMapping的 value为空 或 类本身为 Controller子类
                    if (hasEmptyMethodLevelMappings ||
                            org.springframework.web.servlet.mvc.Controller.class.isAssignableFrom(handlerType)) {
                        addUrlsForPath(urls, typeLevelPattern);
                    }
                }
                return StringUtils.toStringArray(urls);
            } else {
                // 类级别 @RequestMapping的 value为空,直接处理方法级别 @RequestMapping
                return determineUrlsForHandlerMethods(handlerType, false);
            }
        }

        // 如果该类没有被 @RequestMapping标识,但被 @Controller标识
        else if (AnnotationUtils.findAnnotation(handlerType, Controller.class) != null) {
            // 直接处理方法级别 @RequestMapping
            return determineUrlsForHandlerMethods(handlerType, false);
        }

        // 该类既没被 @RequestMapping标识,也没被 @Controller标识
        else {
            return null;
        }
    }


    protected String[] determineUrlsForHandlerMethods(Class<?> handlerType,
                                                      final boolean hasTypeLevelMapping) {
        String[] subclassResult = determineUrlsForHandlerMethods(handlerType);
        if (subclassResult != null) {
            return subclassResult;
        }

        final Set<String> urls = new LinkedHashSet<String>();
        Set<Class<?>> handlerTypes = new LinkedHashSet<Class<?>>();
        handlerTypes.add(handlerType);
        handlerTypes.addAll(Arrays.asList(handlerType.getInterfaces()));
        // 遍历自身和所有父接口
        for (Class<?> currentHandlerType : handlerTypes) {

            // 参数一:需要扫描的对象
            // 参数二:回调实现,会将 currentHandlerType下的所有方法(以及父类),传入 doWith处理
            // 参数三:过滤匹配规则的方法。过滤掉桥接方法以及 Object类下的所有方法
            ReflectionUtils.doWithMethods(currentHandlerType, new ReflectionUtils.MethodCallback() {
                @Override
                public void doWith(Method method) {
                    // 所有被 @RequestMapping接口标记的方法
                    RequestMapping mapping = AnnotationUtils.findAnnotation(method, RequestMapping.class);
                    if (mapping != null) {
                        // 获取 @RequestMapping的 value
                        String[] mappedPatterns = mapping.value();
                        if (mappedPatterns.length > 0) {
                            // 遍历 value数组
                            for (String mappedPattern : mappedPatterns) {
                                // 没有类级别 @RequestMapping,且方法指定的 url没有以“/”开头,默认加上
                                if (!hasTypeLevelMapping && !mappedPattern.startsWith("/")) {
                                    mappedPattern = "/" + mappedPattern;
                                }
                                // 放入 url列表
                                addUrlsForPath(urls, mappedPattern);
                            }
                        } else if (hasTypeLevelMapping) {
                            // 如果方法 @RequestMapping的 value为空
                            urls.add(null);
                        }
                    }
                }
            }, ReflectionUtils.USER_DECLARED_METHODS);
        }
        return StringUtils.toStringArray(urls);
    }


    protected void addUrlsForPath(Set<String> urls, String path) {
        urls.add(path);
        if (this.useDefaultSuffixPattern && path.indexOf('.') == -1 && !path.endsWith("/")) {
            urls.add(path + ".*");
            urls.add(path + "/");
        }
    }
}

    这是 Spring 3.2 版本之前对于注解标识 Handler的支持类。从上面的源码,我们可以看出,处理是区分类级别注解、方法级别注解,在类上声明的注解属性将作用于该类所有的接口方法(最后会调用方法将两者合并)。最后 determineUrlsForHandler 返回的将会是组装好的 url 全集。

    现在这个类已被标识废弃,代替者为 RequestMappingHandlerMapping。在了解它之前,我们需要了解另一分支,也是 RequestMappingHandlerMapping 的抽象父类。

 

AbstractHandlerMethodMapping

public abstract class AbstractHandlerMethodMapping<T> 
                           extends AbstractHandlerMapping implements InitializingBean {

    private boolean detectHandlerMethodsInAncestorContexts = false;

    // 内部类,用于注册 url和 HandlerMethod的映射关系
    private final MappingRegistry mappingRegistry = new MappingRegistry();

    @Override
    public void afterPropertiesSet() {
        initHandlerMethods();
    }

    protected void initHandlerMethods() {
        if (logger.isDebugEnabled()) {
            logger.debug("Looking for request mappings in application context: " + getApplicationContext());
        }

        // 获取子容器中所有 Object类型的 beanName
        // detectHandlerMethodsInAncestorContexts默认为 false,即只探测子容器(mvc)
        String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
                BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) : getApplicationContext().getBeanNamesForType(Object.class));

        // 遍历注册的 beanName
        for (String beanName : beanNames) {
            if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
                Class<?> beanType = null;
                try {
                    beanType = getApplicationContext().getType(beanName);
                } catch (Throwable ex) {
                    // 对于无法解析的类型,会忽略
                    if (logger.isDebugEnabled()) {
                        logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
                    }
                }
                // isHandler由不同策略的子类实现
                if (beanType != null && isHandler(beanType)) {
                    // 会探测满足类型的 Handler下的所有方法
                    detectHandlerMethods(beanName);
                }
            }
        }
        // 空实现:子类可扩展
        handlerMethodsInitialized(getHandlerMethods());
    }

    protected void detectHandlerMethods(final Object handler) {

        // 获取 Handler类型
        Class<?> handlerType = (handler instanceof String ?
                getApplicationContext().getType((String) handler) : handler.getClass());
        // 获取原本的类型,以防个别会被 Ciglib增强代理
        final Class<?> userType = ClassUtils.getUserClass(handlerType);

        // MethodIntrospector.selectMethods几乎是对上面 
        // DefaultAnnotationHandlerMapping.determineUrlsForHandlerMethods的封装
        // 这里不做展开讲解,大致做的就是把 @RequestMapping标识的方法找出来
        // key-就是符合要求的方法,value-@RequestMapping的属性封装的 RequestMappingInfo对象
        Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
                new MethodIntrospector.MetadataLookup<T>() {
                    @Override
                    public T inspect(Method method) {
                        try {
                            // 子类实现
                            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);
        }
        for (Map.Entry<Method, T> entry : methods.entrySet()) {
            // 挑选一个可调用的方法
            Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);
            T mapping = entry.getValue();
            // 注册 HandlerMethod
            registerHandlerMethod(handler, invocableMethod, mapping);
        }
    }

    protected void registerHandlerMethod(Object handler, Method method, T mapping) {
        this.mappingRegistry.register(mapping, handler, method);
    }

    class MappingRegistry {

        // 存放 RequestMappingInfo和 MappingRegistration映射关系
        private final Map<T, MappingRegistration<T>> registry = new HashMap<T, MappingRegistration<T>>();

        // 存放 RequestMappingInfo和 HandlerMethod映射关系
        // 校验以防止同一个 RequestMappingInfo对应多个 HandlerMethod
        private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<T, HandlerMethod>();
        
        // 存放直接
        private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<String, T>();

        //
        private final Map<String, List<HandlerMethod>> nameLookup =
                new ConcurrentHashMap<String, List<HandlerMethod>>();

        // 存放 HandlerMethod和跨域注解 @CrossOrigin配置的映射关系
        private final Map<HandlerMethod, CorsConfiguration> corsLookup =
                new ConcurrentHashMap<HandlerMethod, CorsConfiguration>();

        private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

        public void register(T mapping, Object handler, Method method) {
            this.readWriteLock.writeLock().lock();
            try {
                // 使用 Handler和 Method封装成 HandlerMethod
                HandlerMethod handlerMethod = createHandlerMethod(handler, method);

                // mappingLookup的校验,目的见注释↑
                assertUniqueMethodMapping(handlerMethod, mapping);

                if (logger.isInfoEnabled()) {
                    logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod);
                }
                // 存储 RequestMappingInfo和 handlerMethod映射关系
                this.mappingLookup.put(mapping, handlerMethod);

                // 筛选出不带通配符(例如 "/*" or "/?")的 url放入 urlLookup
                // urlLookup为 MultiValueMap类型,即 Map<String,List<T>>
                // key:@RequestMapping指定的不带通配符的属性"value",value:List<RequestMappingInfo>
                // 这种一对多的结构是预防,会有多个方法对应相同的请求路径(比如 restful风格的接口)
                List<String> directUrls = getDirectUrls(mapping);
                for (String url : directUrls) {
                    this.urlLookup.add(url, mapping);
                }

                String name = null;
                // 默认使用 RequestMappingInfoHandlerMethodMappingNamingStrategy
                if (getNamingStrategy() != null) {
                    // 如果 mapping未指定 name,则使用 类名中的大写字母组合#方法名
                    // 例如:UserController.test() ——> UC#test()
                    name = getNamingStrategy().getName(handlerMethod, mapping);
                    // 放入 nameLookup
                    addMappingName(name, handlerMethod);
                }

                // @CrossOrigin支持,用于支持请求跨域
                CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
                if (corsConfig != null) {
                    this.corsLookup.put(handlerMethod, corsConfig);
                }

                // 放入 registry
                this.registry.put(mapping, new MappingRegistration<T>(mapping, handlerMethod, directUrls, name));
            } finally {
                this.readWriteLock.writeLock().unlock();
            }
        }
    }
}

    从该分支抽象类的命名也能够看出,这个分支将具体的处理器 handler 和 具体调用的方法 method 封装成了 HandlerMethod 类型对象,很多映射关系都跟该对象有关。

    与另一个分支不同的是,该分支不是通过重写 initApplicationContext 来探测处理器的,它依旧沿用了父类该方法的逻辑去初始化“拦截器”,但探测处理器(Handler)则是利用了另一个生命周期接口 InitializingBean.afterPropertiesSet。该方法在 bean 的初始化逻辑中被调用。(见 实例创建(下)invokeInitMethods 方法)

    接下来来看看 RequestMappingHandlerMapping 是如何实现 isHandler  getMappingForMethod,来判断哪些才实例能作为 Handler ,以及对 RequestMappingInfo 的封装。

RequestMappingHandlerMapping
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
        implements MatchableHandlerMapping, EmbeddedValueResolverAware {

    @Override
    protected boolean isHandler(Class<?> beanType) {
        // 被 @Controller或 @RequestMapping标识的类
        return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
                AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
    }

    /**
     * 如果方法被标识 @RequestMapping,则封装成 RequestMappingInfo
     * 如果方法未被标识 @RequestMapping,则返回 null
     */
    @Override
    protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
        // 用方法上的 @RequestMapping属性值封装 RequestMappingInfo
        RequestMappingInfo info = createRequestMappingInfo(method);
        if (info != null) {
            // 用类上的 @RequestMapping属性值封装 RequestMappingInfo
            RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
            if (typeInfo != null) {
                // 将类级别和方法级别的结合,返回新的 RequestMappingInfo
                // 这里可以看出类级别的适用于该类下所有的方法
                info = typeInfo.combine(info);
            }
        }
        return info;
    }

    private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
        RequestMapping requestMapping = 
             AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
        RequestCondition<?> condition = (element instanceof Class ?
                getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
        // 如果方法未被标识 @RequestMapping,则返回 null
        return (requestMapping != null ? 
                createRequestMappingInfo(requestMapping, condition) : null);
    }

    protected RequestMappingInfo createRequestMappingInfo(
            RequestMapping requestMapping, RequestCondition<?> customCondition) {
        // 用方法上的 @RequestMapping属性值封装 RequestMappingInfo
        return RequestMappingInfo
                .paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
                .methods(requestMapping.method())
                .params(requestMapping.params())
                .headers(requestMapping.headers())
                .consumes(requestMapping.consumes())
                .produces(requestMapping.produces())
                .mappingName(requestMapping.name())
                .customCondition(customCondition)
                .options(this.config)
                .build();
    }
}

    RequestMappingHandlerMapping 作为 DefaultAnnotationHandlerMapping (废弃)的替代者,判定标准依然是 @Controller 或 @RequestMapping,讲到这里,大家应该就明白我们平时使用的这两个注解究竟是怎样被框架所识别了。

 

总结

    通过对两个分支的梳理,应该能清楚的看到 SpringMVC 初始化映射关系的时机,以及具体都做了哪些逻辑处理。正是这里将请求的 url 和真正被调用的 Handler建立起了联系,之后的请求就能正确的找到处理器(Handler)。

转载于:https://my.oschina.net/marvelcode/blog/1837044

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值