SpringMVC源码 3.2 DispatchServlet 组件初始化

SpringMVC源码 3.2 DispatchServlet 组件初始化


DispatchServlet还剩下最后的刷新,onRefresh方法。在这个方法中主要初始了一些DispatchServlet中需要使用的一些组件。
例如各种Resolver、HandlerMapping、HandlerAdapter、HandlerExceptionResolvers等。

onRefresh()方法是FramworkServlet提供的模板方法,在子类DispatchServlet中进行了重写,主要用于刷新Spring在web功能实现中所必须提供的全局变量。9个必要组件。
@Override
protected void onRefresh(ApplicationContext context) {
     initStrategies(context);
}

/**
 * Initialize the strategy objects that this servlet uses.
 * <p>May be overridden in subclasses in order to initialize further strategy objects.
 */
protected void initStrategies(ApplicationContext context) {
     initMultipartResolver(context);
     initLocaleResolver(context);
     initThemeResolver(context);
     initHandlerMappings(context);
     initHandlerAdapters(context);
     initHandlerExceptionResolvers(context);
     initRequestToViewNameTranslator(context);
     initViewResolvers(context);
     initFlashMapManager(context);
}

(1)初始化MultipartResolver
在Spring中MultipartResolver是用来处理上传文件的。默认情况下,Spring是没有Multipart处理的。如果想使用Spring的multipart,则需要在上下文中添加multipart解析器,这样每个请求都会被检查是否包含multipart,如果请求中有multipart,那么上下文中的MultipartResolver就会处理他。常用配置:
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
     <property name="maximumFileSize"><value>100000</value></property>
</bean>
CommonsMultipartResolver还提供了其他功能帮助完成上传文件
首先通过context.getBean在容器中按注册时的名称和类型进行查找。这里是“multipartResolver”名称或者MultipartResolver.class类型。所以需要在SpringMVC的配置文件中添加相应类型的组件,容器就能自动找到。如果找不则为null。
注意:这边的context是FrameworkServlet中创建的WebApplicationContext,不是在ContextLoader中创建的。
private void initMultipartResolver(ApplicationContext context) {
     try {
           this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
           if (logger.isDebugEnabled()) {
                logger.debug("Using MultipartResolver [" + this.multipartResolver + "]");
           }
     }catch (NoSuchBeanDefinitionException ex) {
           // Default is no multipart resolver.
           this.multipartResolver = null;
           if (logger.isDebugEnabled()) {
                logger.debug("Unable to locate MultipartResolver with name '" + MULTIPART_RESOLVER_BEAN_NAME +
                           "': no multipart request handling provided");
           }
     }
}

(1)初始化LocaleResolver
首先通过context.getBean在容器中按注册时的名称和类型进行查找。这里是“localeResolver”名称或者LocaleResolver.class类型。如果在容器中找不到,这通过getDefaultStrategy(context, LocaleResolver.class);获取默认的组件。
private void initLocaleResolver(ApplicationContext context) {
     try {
           this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
           if (logger.isDebugEnabled()) {
                logger.debug("Using LocaleResolver [" + this.localeResolver + "]");
           }
     }catch (NoSuchBeanDefinitionException ex) {
           // We need to use the default.
           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 + "]");
           }
     }
}
如何获取默认的组件
调用了getDefaultStrategies方法,返回的是个List。List的size必须是1,并取第一个返回值。
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);
}

通过Class为key,从strategyInterface获取对应的value。value值包含了各个初始化组件类的默认值。通过反创建出对应的实例。
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
     String key = strategyInterface.getName();
     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) {
                    .....
                }catch (LinkageError err) {
                    .....
                }
           }
           return strategies;
     }else {
           return new LinkedList<T>();
     }
}
存在一段static代码块,用于加载defaultStrategies。读取DispatchServlet同级目录(org.springframework.web.servlet)下的DispatcherServlet.properties的文件。
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());
     }
}

可以看到这里定义了不同组件的类型,一共定义了8个。因为MultipartResolver是不需要的,默认返回为null。因为并不是每个应用都是需要上传文件组件的。
注意默认配置并不是最优配置,也不是spring的推荐配置。只是在没有配置的时候有个默认值,不至于空着。

对于MultipartResolver,LocaleResolver,ThemeResolver,RequestToViewNameTranslator,FlashMapManager这五个组件,只需要一个类。处理逻辑也很简单,首先从context.getBean能不能获取,不能获取则使用DispatcherServlet.properties中的默认值。
但是对于另外四个组件,逻辑就比较复杂,会判断detectAll-----值是否为true,如果是true则从所有的相关的定义类(在beanDefinitionName变量中保存)。如果为false在从context.getBean。在获取不到则使用默认值。具体在后面介绍


# 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

(3)初始化ThemeResolver


(4)初始化HandlerMappings
客户端发出Requet时DispatchServlet会将Request提交给HandlerMapping,然后HandlerMapping根据WebApplicationContext的配置回传给DispatchServlet相应的Controller。我们可以为DispatchServlet提供给多个HandlerMapping使用。我们会将制定的HandlerMapping的优先级进行排序,DispatchServlet选用过程中会使用优先级高的。如果当前的HandlerMapping能返回可用的Handler,那DispatchServlet就使用返回的Handler处理web请求,不再询问其他的HandlerMapping。如果不行则询问下一个HandlerMapping,直到获取一个可用的Handler。
private void initHandlerMappings(ApplicationContext context) {
     this.handlerMappings = null;
     if (this.detectAllHandlerMappings) {
           // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
           Map<String, HandlerMapping> matchingBeans =
                     BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
           if (!matchingBeans.isEmpty()) {
                this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
                // We keep HandlerMappings in sorted order.
                OrderComparator.sort(this.handlerMappings);
           }
     }else {
           try {
                HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
                this.handlerMappings = Collections.singletonList(hm);
           }catch (NoSuchBeanDefinitionException ex) {
                // Ignore, we'll add a default HandlerMapping later.
           }
     }
     // Ensure we have at least one HandlerMapping, by registering
     // a default HandlerMapping if no other mappings are found.
     if (this.handlerMappings == null) {
           this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
           if (logger.isDebugEnabled()) {
                logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
           }
     }
}



默认情况下,会加载当前系统中所有实现HandlerMapping接口的bean。(其实也不是全部)。matchingBeans返回的是三个HandlerMapping。
org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping@45451b28
org.springframework.web.servlet.handler.SimpleUrlHandlerMapping@223a93da
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping@7f957ed8(替换了原来的DefaultAnnotationHandlerMapping
从哪里确定就是这三个bean那,会在beanDefinitionNames中去匹配。
只希望SpringMVC加载指定的HandlerMapping,可以修改web.xml中DispatchServlet的初始化参数,将detectAllHandlerMappings设置为false。此时SpringMVC会查找名为“HandlerMapping”的bean,作为当前系统的唯一HandlerMapping。如果没有在spring-serlvet.xml配置文件中定义handlerMapping的话,就会使用DispatchServlet.properties文件中的默认值。
<init-param>
     <param-name>detectAllHandlerMappings</param-name>
     <param-value>false</param-value>
</init-param>



(5)初始化HandlerAdapters

初始化HanlerAdapter的逻辑和HandlerMapping的逻辑差不多。对应的值变成了detectAllHandlerAdapter。
全部的HandlerAdapter也变成了下面这三个。
org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter@6c45f317
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter@1a971906
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter@7766dd27


剩下几个的初始化都和以上的差不多,只是每个组件的功能不同。不同组件的具体功能以后再写。
(6)初始化HandlerExceptionResolvers


(7)初始化RequestToViewNameTranslator


(8)初始化ViewResolvers


(9)初始化FlashManager




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章