SpringMVC源码研读(二) 初始化映射关系

上一篇文章主要记录了springmvc父子容器初始化的整体脉络,许多细节问题都没有深入剖析。springmvc能够自动匹配请求url,查找到对应的类与方法进行处理,这一功能也是需要在初始化阶段进行一些准备的。这篇文章主要对映射关系的初始化做一些研究与记录。

(一)HandlerMapping

HandlerMapping的工作就是为每个请求找到一个合适的处理器handler,其实现机制简单来说就是维持了一个从url到请求处理器关系的Map结构。
HandlerMapping接口及实现类如下:


HandlerMapping结构

通过上图我们可以看到两个主要抽象类AbstractUrlHandlerMapping和AbstractHandlerMethodMapping。

  • AbstractHandlerMethodMapping系列是将具体的Method作为Handler来使用的,也是我们用的最多的。比如经常使用的@RequestMapping所注释的方法就是这种Handler。
  • AbstractUrlHandlerMapping是通过url来进行匹配的,大致原理是将url与对应的Handler保存在一个map中。

(二)RequestMappingHandlerMapping

在springmvc的配置文件中,这一行配置想必大家都不陌生。

<mvc:annotation-driven/>

springmvc在解析到这行配置时,会调用AnnotationDrivenBeanDefinitionParser的parse方法,其中就对RequestMappingHandlerMapping进行了注册。


标签处理类

RequestMappingHandlerMapping的父类AbstractHandlerMethodMapping实现了InitializingBean接口,并重写了afterPropertiesSet()方法,进行了一些初始化操作,源码如下:

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

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

    protected void initHandlerMethods() {
        if (logger.isDebugEnabled()) {
            logger.debug("Looking for request mappings in application context: " + getApplicationContext());
        }
      //获取springmvc上下文的所有注册的bean
        String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
                BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
                getApplicationContext().getBeanNamesForType(Object.class));

        for (String beanName : beanNames) {
            if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
                Class<?> beanType = null;
                try {
                    beanType = getApplicationContext().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);
                    }
                }
        //isHandler由子类RequestMappingHandlerMapping重写,寻找符合拥有@Controller注解或是@RequestNMapping注解的类
                if (beanType != null && isHandler(beanType)) {
                 //主要执行步骤,检测HandlerMethod形成映射关系
                    detectHandlerMethods(beanName);
                }
            }
        }
        handlerMethodsInitialized(getHandlerMethods());
    }

    protected void detectHandlerMethods(final Object handler) {
        Class<?> handlerType = (handler instanceof String ?
                getApplicationContext().getType((String) handler) : handler.getClass());
        final Class<?> userType = ClassUtils.getUserClass(handlerType);
       //寻找bean中方法的映射关系
        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);
        }
        //对查找到的映射关系进行注册,保存至内部类mappingRegistry对象中
        for (Map.Entry<Method, T> entry : methods.entrySet()) {
            registerHandlerMethod(handler, entry.getKey(), entry.getValue());
        }
    }
 }

抽象方法getMappingForMethod由子类RequestMappingHandlerMapping实现,将Class上的@RequestMapping和Method上的@RequestMapping组装成RequestMappingInfo对象返回。源码如下:

    @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) {
                //将类与方法的映射关系相组合
                info = typeInfo.combine(info);
            }
        }
        return info;
    }

    private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
        //获取@RequestMapping注解信息
        RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
        RequestCondition<?> condition = (element instanceof Class<?> ?
                getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
        return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
    }

所以RequestMappingHandlerMapping保存的映射关系,实际上是键值对Map<RequestMappingInfo, HandlerMethod> mappingLookup。对于路径中不存在"?"和"*"的url,还会保存匹配关系到MultiValueMap<String, RequestMappingInfo> urlLookup中,其中key为方法的directUrl。

(三)SimpleUrlHandlerMapping

上一节讲的是AbstractHandlerMethodMapping实现类,接下来讲一下AbstractUrlHandlerMapping的实现类SimpleUrlHandlerMapping,通常用于静态资源映射。
在进行springmvc的配置时,通常我们会配置一个dispatcher servlet用于处理对应的URL。配置如下:


springmvc配置

由于配置的拦截路径是"/",所有路径的请求都会转由DispatcherServlet处理。当试图访问静态资源路径时,由于没有对应的controller来处理请求,会导致访问失败。
需要在mvc配置文件中加入如下类似配置来解决静态资源映射问题。

<mvc:resources mapping="/assets/**" location="/assets/"/>

那这是怎么生效的呢?


标签处理

mvc:resources标签对应的解析器是ResourcesBeanDefinitionParser。在parse方法中,解析器注册了SimpleUrlHandlerMapping,用于保存静态资源url与对应处理器的映射关系。SimpleUrlHandlerMapping的处理优先级是最低的,在其他HandlerMapping找不到映射关系时才会转由它处理。源码如下:

    @Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        Object source = parserContext.extractSource(element);

        registerUrlProvider(parserContext, source);
                //注册静态资源处理器ResourceHttpRequestHandler
        String resourceHandlerName = registerResourceHandler(parserContext, element, source);
        if (resourceHandlerName == null) {
            return null;
        }

        Map<String, String> urlMap = new ManagedMap<String, String>();
        String resourceRequestPath = element.getAttribute("mapping");
        if (!StringUtils.hasText(resourceRequestPath)) {
            parserContext.getReaderContext().error("The 'mapping' attribute is required.", parserContext.extractSource(element));
            return null;
        }
        urlMap.put(resourceRequestPath, resourceHandlerName);

        RuntimeBeanReference pathMatcherRef = MvcNamespaceUtils.registerPathMatcher(null, parserContext, source);
        RuntimeBeanReference pathHelperRef = MvcNamespaceUtils.registerUrlPathHelper(null, parserContext, source);
                //注册SimpleUrlHandlerMapping
        RootBeanDefinition handlerMappingDef = new RootBeanDefinition(SimpleUrlHandlerMapping.class);
        handlerMappingDef.setSource(source);
        handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
                //key为配置文件中的mapping,value为处理器ResourceHttpRequestHandler
        handlerMappingDef.getPropertyValues().add("urlMap", urlMap);
        handlerMappingDef.getPropertyValues().add("pathMatcher", pathMatcherRef).add("urlPathHelper", pathHelperRef);

        String order = element.getAttribute("order");
                //支持自定义mapping优先级,默认设置order为2147483646
        handlerMappingDef.getPropertyValues().add("order", StringUtils.hasText(order) ? order : Ordered.LOWEST_PRECEDENCE - 1);

        RuntimeBeanReference corsConfigurationsRef = MvcNamespaceUtils.registerCorsConfigurations(null, parserContext, source);
        handlerMappingDef.getPropertyValues().add("corsConfigurations", corsConfigurationsRef);

        String beanName = parserContext.getReaderContext().generateBeanName(handlerMappingDef);
        parserContext.getRegistry().registerBeanDefinition(beanName, handlerMappingDef);
        parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, beanName));
        MvcNamespaceUtils.registerDefaultComponents(parserContext, source);

        return null;
    }

(四)总结

研读了springmvc中两个运用比较多的HandlerMapping的初始化过程,随后DispatcherServlet执行initHandlerMappings方法,直接从ApplicationContext容器中获取HandlerMapping列表,经由order排序后注入。


DispatcherServlet注入handlerMappings

DispatcherServlet初始化后的handlerMappings

为后续文章分析DispatcherServlet的分发与处理请求过程打下基础。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容