码迷,mamicode.com
首页 > 编程语言 > 详细

Java框架-Spring MVC理解005-DispatcherServlet

时间:2018-05-21 23:01:50      阅读:245      评论:0      收藏:0      [点我收藏+]

标签:path   col   不可   private   classes   sim   imp   list   cat   

DispatcherServlet

  通过源码我们可以看到,onRefresh方法是DispatcherServlet的入口方法。onRefresh中简单地调用了initStrategies,在initStrategies中调用了9个初始化方法。

 

// org.springframework.web.servlet.DispatcherServlet
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);
    initFlashMapManager(context);
}

 

我们会想:为什么不将initStrategies的具体实现直接写到onRefresh中呢?initStrategies方法不是多余的吗?

  其实这主要是分层的原因,onRefresh是用来刷新容器的,initStrategies用来初始化一些策略组件。如果把initStrategies里面的代码直接写到onRefresh里面,对于程序的运行也没有影响,不过这样一来,如果在onRefresh中想再添加别的功能,就会没有将其单独写一个方法出来逻辑清晰,不过这并不是最重要的,更重要的是,如果在别的地方也需要调用initStrategies方法(如需要修改一些策略后进行热部署),但initStrategies没独立出来,就只能调用onRefresh,那样在onRefresh增加了新功能的时候就麻烦了。另外单独将initStrategies写出来还可以被子类覆盖,使用新的模式进行初始化。
  initStrategies的具体内容非常简单,就是初始化的9个组件,下面以LocaleResolver为例来分析具体的初始化方式://初始化区域解析器

// org.springframework.web.servlet.DispatcherServlet
private void initLocaleResolver(ApplicationContext context) {
    try {
        // 在context中获取
        this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
        if (logger.isDebugEnabled()) {
            logger.debug("Using LocaleResolver [" + this.localeResolver + "]");
        }
    }catch (NoSuchBeanDefinitionException ex) {
        // 使用默认策略
        this.localeResolver = getDefaultStrategy(context, LocaleResolver.class);
        if (logger.isDebugEnabled()) {
            logger.debug("Unable to locate LocaleResolver with name ‘" + LOCALE_RESOLVER_BEAN_NAME +
            "‘: using default [" + this.localeResolver + "]");
        }
    }
}

初始化方式分两步:首先通过context.getBean在容器里面按注册时的名称或类型(这里指“localeResolver”名称或者LocaleResolver.class类型)进行查找,所以在Spring MVC的配置文件中只需要配置相应类型的组件,容器就可以自动找到。如果找不到就调用getDefaultStrategy按照类型获取默认的组件。需要注意的是,这里的context指的是Frame-workServlet中创建的WebApplicationContext,而不是ServletContext。下面介绍getDefault-Strategy是怎样获取默认组件的。

// org.springframework.web.servlet.DispatcherServlet
protected<T> T getDefaultStrategy(ApplicationContext context, Class<T> strategyInterface) {
    List<T> strategies = getDefaultStrategies(context, strategyInterface);
    if (strategies.size() != 1) {
        throw new BeanInitializationException(
            "DispatcherServlet needs exactly 1 strategy for interface [" + strategyInterface.getName() + "]");
    }
    return strategies.get(0);
}
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
    String key = strategyInterface.getName();
    //从defaultStrategies获取所需策略的类型
    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());
                Object strategy = createDefaultStrategy(context, clazz);
                strategies.add((T) strategy);
            }catch (ClassNotFoundException ex) {
                throw new BeanInitializationException(
                    "Could not find DispatcherServlet‘s default strategy class [" + className +
                        "] for interface [" + key + "]", ex);
            }catch (LinkageError err) {
                throw new BeanInitializationException(
                    "Error loading DispatcherServlet‘s default strategy class [" + className +
                        "] for interface [" + key + "]: problem with class file or dependent class", err);
            }
        }
        return strategies;
    }else {
        return new LinkedList<T>();
    }
}

  可以看到getDefaultStrategy中调用了getDefaultStrategies,后者返回的是List,这是因为HandlerMapping等组件可以有多个,所以定义了getDefaultStrategies方法,getDefaultStrategy直接调用了getDefaultStrategies方法,并返回返回值的第一个结果。getDefaultStrategies中实际执行创建的方法是ClassUtils.forName,它需要的参数是class-Name,所以最重要的是看className怎么来的,找到了className的来源,也就可以理解默认初始化的方式。className来自classNames,classNames又来自value,而value来自default-Strategies.getProperty(key)。所以关键点就在defaultStrategies中,defaultStrategies是一个静态属性,在static块中进行初始化的。

// org.springframework.web.servlet.DispatcherServlet
private static final Properties defaultStrategies;

static {
    try {
        ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
        defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
    }catch (IOException ex) { throw new IllegalStateException("Could not load ‘DispatcherServlet.properties‘: " + ex.getMessage());
    }
}
# org.springframework.web.DispatcherServlet.properties
# Default implementation classes for DispatcherServlet‘s strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.//这些配置是固定的,开发者不可以定制

org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,
org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,    org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,    org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter

org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,    org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,    org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator

org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

  我们看到defaultStrategies是DispatcherServlet类所在包下的DEFAULT_STRATEGIES_PATH文件里定义的属性,DEFAULT_STRATEGIES_PATH的值是DispatcherServlet.properties。所以defaultStrategies里面存放的是org.springframework.web.DispatcherServlet.properties里面定义的键值对,代码如上。

  可以看到,这里确实定义了不同组件的类型,一共定义了8个组件,处理上传组件Multi-partResolver是没有默认配置的,这也很容易理解,并不是每个应用都需要上传功能,即使需要上传也不一定就要使用MultipartResolver,所以MultipartResolver不需要默认配置。另外HandlerMapping、HandlerAdapter和HandlerExceptionResolver都配置了多个,其实View-Resolver也可以有多个,只是默认的配置只有一个。

  这里需要注意两个问题:首先默认配置并不是最优配置,也不是spring的推荐配置,只是在没有配置的时候可以有个默认值,不至于空着。里面的有些默认配置甚至已经被标注为@Deprecated,表示已弃用,如DefaultAnnotationHandlerMapping、Annotation-MethodHandlerSolver不需要默认配置。另外HandlerMapping、andlerAdapter和HandlerExceptionResolver都配置了多个,其实View-Resolver也可以有多个,只是默认的配置只有一个。

DispatcherServlet的创建过程主要是对9大组件进行初始化,具体每个组件的作用后面具体讲解。

 

Java框架-Spring MVC理解005-DispatcherServlet

标签:path   col   不可   private   classes   sim   imp   list   cat   

原文地址:https://www.cnblogs.com/yulibo/p/9069449.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!