04.SpringMVC 初始化 - DispatcherServlet

基本概念

在配置 SpringMVC 的 Servlet 时,实际定义的就是 DispatcherServlet 类,它是 FrameworkServlet 的子类,具体的继承关系如下:

这里写图片描述

DispatcherServlet 重写了父类的 onRefresh 方法,说明它也负责 SpringMVC 的初始化工作。


原理分析

首先来看该类的 onRefresh 方法,发现真正的初始化过程在 initStrategies 中定义。

protected void onRefresh(ApplicationContext context) {
    initStrategies(context);
}

protected void initStrategies(ApplicationContext context) {

    // 初始化多文件上传解析器
    initMultipartResolver(context);

    // 初始化区域解析器
    initLocaleResolver(context);

    // 初始化主题解析器
    initThemeResolver(context);

    // 初始化处理器映射集
    initHandlerMappings(context);

    // 初始化处理器适配器
    initHandlerAdapters(context);

    // 初始化处理器异常解析器
    initHandlerExceptionResolvers(context);

    // 初始化请求到视图名转换器
    initRequestToViewNameTranslator(context);

    // 初始化视图解析器
    initViewResolvers(context);

    // 初始化 flash 映射管理器
    initFlashMapManager(context);
}

观察代码,可以发现它负责初始化 SpringMVC 的各种组件。

需要注意的是,唯一入参 context 就是在其父类初始化过程创建的 SpringMVC 容器。


默认策略

1.作用

在 SrpingMVC 中存在一个默认的属性文件:DispatcherServlet.properties。它的 key 是接口名,value 是实现类的名称。具体内容如下:

这里写图片描述

它被称作默认策略、默认配置。作用是:

  • 当 SrpingMVC 中在初始化组件时,若在容器中找不到定义的相关组件,则从默认策略中获取。

2.初始化

默认策略的初始化过程在 DispatcherServlet 静态域中定义,它最终被加载到成员变量 defaultStrategies

private static final Properties defaultStrategies;
private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";
static {
    try {
        // 加载默认策略的属性文件
        ClassPathResource resource = 
            new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
        defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
    }catch (IOException ex) {
        // 抛出异常...
    }
}

3.调用过程

在该类中定义了两种获取默认策略的方式:

  • 获取同种类型的一个默认组件:getDefaultStrategy
  • 获取同种类型的所有默认组件:getDefaultStrategies

  • getDefaultStrategy
protected <T> T getDefaultStrategy(ApplicationContext context, Class<T> strategyInterface) {
    // 关键 -> 具体实现还是得通过 getDefaultStrategies
    List<T> strategies = getDefaultStrategies(context, strategyInterface);

    if (strategies.size() != 1) {
        // 抛出异常...
    }
    return strategies.get(0);
}
  • getDefaultStrategies
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {

    // 取得默认策略(属性文件)的 key、value
    String key = strategyInterface.getName();
    String value = defaultStrategies.getProperty(key);

    if (value != null) {
        // 拆分成数组,可能一个接口存在多个实现类,即 value 中包含多个类
        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());

                // 在 SrpingMVC 容器中创建 Bean 
                Object strategy = createDefaultStrategy(context, clazz);

                // 将创建的 Bean 添加进集合
                strategies.add((T) strategy);

            }catch (ClassNotFoundException ex) {
                // 抛出异常...
            }catch (LinkageError err) {
                // 抛出异常...
            }
        }
        return strategies;
    }else {
        return new LinkedList<T>();
    }
}

流程探究

由于在 DispatcherServlet 中初始化各种组件的流程大致相同:

  • 从 SpringMVC 容器中手动获取指定名称的 Bean;不存在则置为空。
  • 从 SpringMVC 容器中手动获取指定名称的 Bean;不存在则从默认策略中查找相同类型的 Bean。
  • 从 SpringMVC 容器中手动获取指定名称的 Bean , 或从所有容器中获取相同类型的 Bean ;不存在则从默认策略中查找相同类型的 Bean。

因此这里只分析 initHandlerMappings 方法,它是上述流程的第三种情况。来看源码:

public static final String HANDLER_MAPPING_BEAN_NAME = "handlerMapping";

private List<HandlerMapping> handlerMappings;

// 表示是否检测所有容器中的 HandlerMapping
private boolean detectAllHandlerMappings = true;

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

    if (this.detectAllHandlerMappings) {
        // 1.1.检测所有所有容器,返回指定类型和子类型的所有 Bean
        Map<String, HandlerMapping> matchingBeans = 
            BeanFactoryUtils.beansOfTypeIncludingAncestors(context, 
                HandlerMapping.class, true, false);

        // 不为空则添加进集合,并排序        
        if (!matchingBeans.isEmpty()) {
            this.handlerMappings = 
                new ArrayList<HandlerMapping>(matchingBeans.values());
            OrderComparator.sort(this.handlerMappings);
        }
    }else {
        try {
            // 1.2.从 SpringMVC 容器中手动获取指定的 Bean,并添加集合
            HandlerMapping hm = 
                context.getBean(HANDLER_MAPPING_BEAN_NAME,HandlerMapping.class);

            this.handlerMappings = Collections.singletonList(hm);

        }catch (NoSuchBeanDefinitionException ex) { }
    }

    // 2.从默认策略中获取相同类型的 Bean 
    if (this.handlerMappings == null) {
        this.handlerMappings = 
            getDefaultStrategies(context, HandlerMapping.class);

        // 省略部分代码...
    }
}

  • BeanFactoryUtils.beansOfTypeIncludingAncestors
public static <T> Map<String, T> beansOfTypeIncludingAncestors(
    ListableBeanFactory lbf, Class<T> type, 
    boolean includeNonSingletons, boolean allowEagerInit) 
    throws BeansException {

    // 省略部分代码...

    Map<String, T> result = new LinkedHashMap<String, T>(4);

    // 1.在当前容器寻找指定类型的bean
    result.putAll(lbf.getBeansOfType(type, includeNonSingletons, allowEagerInit));

    // 2.在它的所有父容器寻找指定类型的bean,
    if (lbf instanceof HierarchicalBeanFactory) {
        HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;
        if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
            // 关键 -> 再次调用该方法,会递归查询,直到找到顶层的容器为止
            Map<String, T> parentResult = 
                beansOfTypeIncludingAncestors(
                (ListableBeanFactory)hbf.getParentBeanFactory(),type,includeNonSingletons, allowEagerInit);

            // 过滤与 result 中相同名称的 Bean 
            for (Map.Entry<String, T> entry : parentResult.entrySet()) {
                String beanName = entry.getKey();
                if (!result.containsKey(beanName) && !hbf.containsLocalBean(beanName)) {
                    result.put(beanName, entry.getValue());
                }
            }
        }
    }
    return result;
}

总结

DispatcherServlet 初始化的工作就是负责初始化各种不同的组件。它好比给 SpringMVC 添加功能模块,一旦功能模块添加完毕,SpringMVC 就可以正常工作。因此 SpringMVC 的初始化工作也到此结束。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

oxf

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值