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

概述

在前一章http://www.javashuo.com/article/p-mowcxnor-kr.html的基础上继续分析,主要完成SimpleUrlHandlerMapping类的原理。web

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

类图

在分析类以前,先了解下类的继承关系,以下图:segmentfault

红框的类就是咱们本章要分析的类。app

建立/初始化

从类图关系上能够看出,SimpleUrlHanderMapping类最终实现了ApplicationContextAware接口,该接口定义了方法setApplicationContext(applicationContext),其做用是实现该接口的类,在Spring实例化类时,自动调用setApplicationContext(applicationContext)方法。ide

ApplicationObjectSupport抽象类实现了ApplicationContextAware接口的setApplicationContext(applicationContext)方法。学习

因为WebApplicationObjectSupport抽象类重写了父类ApplicationObjectSupport的initApplicationContext(context)方法,因此此时会调用WebApplicationObjectSupport的initAppliationContext(context)方法,在该方法中经过suppr调用父类的initApplicationContext(context)方法,该方法经过模板方法模式最终调到SimpleUrlHandlerMapping类的initApplicationContext()方法。this

整个建立涉及的类比较多,过程比较复杂,文字描述也很乏味,因此我画了调用时序图,可供你们参考:url

分析到此,咱们就找到了SimpleUrlHandlerMapping类的入口方法,即本类的initApplicationContext()方法。spa

分析

  • SimpleUrlHandlerMapping

该类间接实现了org.springframework.web.servlet.HandlerMapping接口,直接实现该接口的是org.springframework.web.servlet.handler.AbstractHandlerMapping抽象类,映射Url与请求handler bean。支持映射bean实例和映射bean名称。源代码以下:debug

public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping {
    // 存储url和bean映射
    private final Map<String, Object> urlMap = new LinkedHashMap<>();
    // 注入property的name为mappings映射
    public void setMappings(Properties mappings) {
        CollectionUtils.mergePropertiesIntoMap(mappings, this.urlMap);
    }
    // 注入property的name为urlMap映射
    public void setUrlMap(Map<String, ?> urlMap) {
        this.urlMap.putAll(urlMap);
    }
    public Map<String, ?> getUrlMap() {
        return this.urlMap;
    }
    // 实例化本类实例入口
    @Override
    public void initApplicationContext() throws BeansException {
        // 调用父类AbstractHandlerMapping的initApplicationContext方法,只要完成拦截器的注册
        super.initApplicationContext();
        // 处理url和bean name,具体注册调用父类完成
        registerHandlers(this.urlMap);
    }
    // 注册映射关系,及将property中的值解析到map对象中,key为url,value为bean id或name
    protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
        if (urlMap.isEmpty()) {
            logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");
        }
        else {
            urlMap.forEach((url, handler) -> {
                // 增长以"/"开头
                if (!url.startsWith("/")) {
                    url = "/" + url;
                }
                // 去除handler bean名称的空格
                if (handler instanceof String) {
                    handler = ((String) handler).trim();
                }
                // 调用父类AbstractUrlHandlerMapping完成映射
                registerHandler(url, handler);
            });
        }
    }

}

从以上代码可知,SimpleUrlHandlerMapping类主要接收用户设定的url与handler的映射关系,其实际的工做都是交由其父类来完成的。

  • AbstractHandlerMapping

在建立初始化SimpleUrlHandlerMapping类时,调用其父类的initApplicationContext()方法,该方法完成拦截器的初始化,代码以下:

@Override
protected void initApplicationContext() throws BeansException {
    // 空实现。
    // 子类可重写此方法以注册额外的拦截器
    extendInterceptors(this.interceptors);
    // 从上下文中查询拦截器并添加到拦截器列表中
    detectMappedInterceptors(this.adaptedInterceptors);
    // 初始化拦截器
    initInterceptors();
}

// 查找实现了MappedInterceptor接口的bean,并添加到映射拦截器列表
protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
    mappedInterceptors.addAll(
            BeanFactoryUtils.beansOfTypeIncludingAncestors(
                    obtainApplicationContext(), MappedInterceptor.class, true, false).values());
}

// 将自定义bean设置到适配拦截器中,bean需实现HandlerInterceptor或WebRequestInterceptor
protected void initInterceptors() {
    if (!this.interceptors.isEmpty()) {
        for (int i = 0; i < this.interceptors.size(); i++) {
            Object interceptor = this.interceptors.get(i);
            if (interceptor == null) {
                throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");
            }
            this.adaptedInterceptors.add(adaptInterceptor(interceptor));
        }
    }
}
  • AbstractUrlHandlerMapping

在建立初始化SimpleUrlHandlerMapping类时,调用AbstractUrlHandlerMapping类的registerHandler(urlPath,handler)方法,该方法源码以下:

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;

    // 不是懒加载,默认为false,即不是,经过配置SimpleUrlHandlerMapping属性lazyInitHandlers的值进行控制
    // 若是不是懒加载而且handler为单例,即从上下文中查询实例处理,此时resolvedHandler为handler实例对象;
    // 若是是懒加载或者handler不是单例,即resolvedHandler为handler逻辑名
    if (!this.lazyInitHandlers && handler instanceof String) {
        String handlerName = (String) handler;
        ApplicationContext applicationContext = obtainApplicationContext();
        // 若是handler是单例,经过bean的scope控制
        if (applicationContext.isSingleton(handlerName)) {
            resolvedHandler = applicationContext.getBean(handlerName);
        }
    }

    Object mappedHandler = this.handlerMap.get(urlPath);
    if (mappedHandler != null) {
        if (mappedHandler != resolvedHandler) {
            throw new IllegalStateException(
                    "Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +
                    "]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");
        }
    }
    else {
        if (urlPath.equals("/")) {
            if (logger.isInfoEnabled()) {
                logger.info("Root mapping to " + getHandlerDescription(handler));
            }
            setRootHandler(resolvedHandler);
        }
        else if (urlPath.equals("/*")) {
            if (logger.isInfoEnabled()) {
                logger.info("Default mapping to " + getHandlerDescription(handler));
            }
            setDefaultHandler(resolvedHandler);
        }
        else {
            // 把url与handler(名称或实例)放入map,以供后续使用
            this.handlerMap.put(urlPath, resolvedHandler);
            if (logger.isInfoEnabled()) {
                logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler));
            }
        }
    }
}

到此,SimpleUrlHandlerMapping类在容器启动期间的初始化完成。

总结

本文分析了SimpleUrlHandlerMapping类初始化过程,其实核心就是把url和handler进行了映射,供后续访问使用,单靠看文章没法掌握。整个过程调用很复杂,你们多debug跟踪,必定能了解其内部的逻辑。你们共勉!

最后建立了qq群方便你们交流,可扫描加入,同时也可加我qq:276420284,共同窗习、共同进步,谢谢!

相关文章
相关标签/搜索