Java框架-Spring MVC理解005-DispatcherServlet

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大组件进行初始化,具体每个组件的作用后面具体讲解。

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

时间: 2024-10-19 10:42:33

Java框架-Spring MVC理解005-DispatcherServlet的相关文章

Java框架-Spring MVC理解001

Spring MVC理解 最近在读一本<看透springMVC>的书,从了解到了一些更加精细系统的知识,边读变分享吧. 1.servlet--Spring MVC的本质 2.Spring MVC其实是一个工具,具体的理解可以分为两步:第一步,了解这个工具是怎么创建出来的:第二步,了解这个工具是怎么用的. 3.前期使用准备:环境的搭建 ①创建WEB项目,导入jar包,Maven项目简单的加入springMVC和servlet的依赖就可以了. //Maven项目加入依赖 <dependenc

Java框架-Spring MVC理解002-关联源代码

Spring MVC-关联源代码 首先说一下我们为什么要关联源代码: 其实,我觉得阅读源码是一个非常好的学习方式,在我们日常工作中或多或少都会接触一些开源代码,比如说最常用的非常流行的Struts.Hibernate.Spring这些框架的源码.这些优秀的源码中有着多年积淀下来的精华,是非常值得我们学习的,不管我们当前是什么水平,通过反复阅读源码能力能有所提升,小到对源码所提供的功能上的使用更加熟练,大到使我们的程序设计更加完美优秀. 具体步骤: 将spring的源代码关联上去.在关联之前首先需

Java框架-Spring MVC应用001-spring MVC配置

spring MVC配置 1.导入jar包 2.添加Web.xml配置文件中关于SpringMVC的配置 <!--configure the setting of springmvcDispatcherServlet and configure the mapping--><!--Spring分为多个文件进行分别的配置,其中在servlet-name中如果没有指定init-param属性,那么系统自动寻找的spring配置文件为[servlet-name]-servlet.xml.--&

java 使用框架Spring MVC批量上传图片

java 使用框架Spring MVC批量上传图片 运行环境:jdk1.8.0_17+tomcat 8 + spring:4.3.4+mybatis:3.2.7+ eclipse ide 2018+maven管理 最近写项目时候用到上传图片文件等功能之前写过没有总结,以及记录等,今天总结下写的批量上传图片功能 前段只需要把所有的选择的图片提交上来即可,后台会逐个处理,并且会在服务器或者当前电脑自己新建一个存放图片的文件夹,在application.properties配置里面你可以指定你所存放图

【Java】Spring MVC 扩展和SSM框架整合

开发web项目通常很多地方需要使用ajax请求来完成相应的功能,比如表单交互或者是复杂的UI设计中数据的传递等等.对于返回结果,我们一般使用JSON对象来表示,那么Spring MVC中如何处理JSON对象? JSON对象的处理 使用@ResponseBody实现数据输出 要使用JSON,所以导一下JSON工具包.JSON工具包,密码4i0l. Controller层代码示例(这里使用的是阿里巴巴的 fastjson): 1 /** 2 * 判断注册时用户编码是否唯一 3 * @param re

java框架spring的依赖注入初步理解

java框架的spring作为整个工程的统领者,可以有效地管理各层的对象,有效的协调运行,当系统西药重构时,可以极大地减少改写代码的量. 依赖注入和控制反转属于同一个概念,在java中当某个类(调用者)需要另一个类(被调用者)的协助时,在以往的程序设计理念中,通常由调用者类创建一个被调用者类的实例(new一个被调用者类),这种new一个对象的方法通常会在java空间中开创一个空间,对java项目整体运行效率会有一定的影响,而且是比较粗鲁的方式.但在spring框架里,创建调用类的工作不再由调用者

SSM框架-----Spring MVC篇

二.Spring MVC 1.1 spring MVC是什么? spring MVC:是一个基于MVC架构,简化的web应用程序开发的框架(属于Spring框架的一部分),通过实现Model-View-Controller模式来很好地将数据.业务与展现进行分离.从这样一个角度来说,Spring MVC和Struts.Struts2非常类似.Spring MVC的设计是围绕DispatcherServlet展开的,每一个请求最先访问的都是DispatcherServlet,DispatcherSe

Java之Spring mvc详解

文章大纲 一.Spring mvc介绍二.Spring mvc代码实战三.项目源码下载四.参考文章 一.Spring mvc介绍 1. 什么是springmvc   springmvc是spring框架的一个模块,springmvc和spring无需通过中间整合层进行整合.springmvc是一个基于mvc的web框架. 2. mvc设计模式在b/s系统 下的应用 3. Spring mvc框架执行流程   第一步:发起请求到前端控制器(DispatcherServlet)  第二步:前端控制器

详解Spring MVC 4之DispatcherServlet

Spring MVC 4与其它MVC框架类似,都是基于请求驱动的,通过中央Servlet处理器,将请求转发到控制器然后完成相应的功能.Spring的 DispatcherServlet与其它MVC框架不同的是,它与Spring的IoC容器集成并允许用户使用Spring的其它特性.     1. DispatcherServlet的作用 Spring MVC 4的工作流如下图所示.DispatcherServlet充当了"前端控制器"的角色. DispatcherServlet实际上就是