SpringMVC 容器初始化时,
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | protected void onRefresh(ApplicationContext context) { this .initStrategies(context); } protected void initStrategies(ApplicationContext context) { this .initMultipartResolver(context); this .initLocaleResolver(context); this .initThemeResolver(context); this .initHandlerMappings(context); this .initHandlerAdapters(context); this .initHandlerExceptionResolvers(context); this .initRequestToViewNameTranslator(context); this .initViewResolvers(context); this .initFlashMapManager(context); } |
. initMultipartResolver 文件上传
源码:
?
1 | this .multipartResolver = (MultipartResolver)context.getBean( "multipartResolver" , MultipartResolver. class ); |
MultipartResolver接口定义如下:
?
1 2 3 4 5 6 7 8 9 10 | public interface MultipartResolver { // 检查请求头是否包含文件流上传 boolean isMultipart(HttpServletRequest var1); // 文件流上传请求解析方法,解析后封装在 MultipartHttpServletRequest 对象中 MultipartHttpServletRequest resolveMultipart(HttpServletRequest var1) throws MultipartException; // void cleanupMultipart(MultipartHttpServletRequest var1); } |
我们知道,相应的请求会被DispatchServlet的doDispatch方法拦截,doDispatch方法中共首先就会调用checkMultipart方法检查请求是否是包含文件流:
?
1 2 3 4 5 6 7 8 9 10 11 | protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException { if ( this .multipartResolver != null && this .multipartResolver.isMultipart(request)) { if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest. class ) != null ) { this .logger.debug( "..." ); } else if ( this .hasMultipartException(request)) { this .logger.debug( "..." ); } else { try { // 具体的文件流解析方法 return this .multipartResolver.resolveMultipart(request); ...<br>} |
配置例子
CommonsFileUploadSupport实现了 MultipartResolver 接口
?
1 2 3 4 5 6 7 8 | //上传文件配置 @Bean (name = "multipartResolver" ) public CommonsFileUploadSupport commonsFileUploadSupport(){ CommonsFileUploadSupport resolver = new CommonsMultipartResolver(); resolver.setMaxInMemorySize( 40960 ); resolver.setMaxUploadSize(10485760000L); return resolver; } |
2. initLocaleResolver 国际化
DispatcherServlet.properties文件:
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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 |
DispatchServlet中定义一个静态的容器初始化默认defaultStrategies方法块,如果用户没有自定义的话,就使用默认的,
?
1 2 3 4 5 6 7 8 9 10 11 12 | static { // Load default strategy implementations from properties file. // This is currently strictly internal and not meant to be customized // by application developers. try { ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet. class ); defaultStrategies = PropertiesLoaderUtils.loadProperties(resource); } catch (IOException ex) { throw new IllegalStateException( "Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage()); } } |
流程:
?
1 2 3 4 5 6 7 8 | 1 . this .localeResolver = getDefaultStrategy(context, LocaleResolver. class ); 2 . List<T> strategies = getDefaultStrategies(context, strategyInterface) 3 . String key = strategyInterface.getName(); String value = defaultStrategies.getProperty(key); // 如果value不为空, 将value转换为class类名称,然后在容器了创建对象 Object strategy = this .createDefaultStrategy(context, clazz); |
pringMVC国际化提供了四个国际化的实现的类AcceptHeaderLocaleResolver(默认),FixedLocaleResolver、CookieLocaleResolver和SessionLocaleResolver。
3. initThemeResolver 主题
主题的实现原理:大概就是把网站版面的css样式表和图片之类的文件和网站的程序进行解耦,程序读取theme的持久化配置,然后找到相应的css样式表和图片,配置网站版面。
要在程序中使用主题,必须设置一个org.springframework.ui.context.ThemeSource
的实现类。SpringMVC IOC容器本身实现了ThemeSource,这个类只是简单的把责任代理给了一个特定的实现类,默认情况下这个代理类是:org.springframework.ui.context.support.ResourceBundleThemeSource
,这个实现类可以从classpath目录下载入一个properties文件。如设置setBasenamePrefix、setDefaultEncoding等。
原理(实践)
如:SpringMVC中一套主题对应一个cool.properties文件,该文件在classpath根目录下, 这个文件列出了主题组成的资源:
?
1 2 | styleSheet=/themes/cool/style.css background=/themes/cool/img/coolBg.jpg |
properties文件中的key是指视图文件中元素的名称,value值主题存放的位置。jsp中可以通过jsp文件可以通过spring:theme来访问这个key,然后找到value(对应的主题)。
ResourceBundleThemeSourceuses(默认的ThemeSource)的作用是根据主题名找到具体的主题,这个例子中就是找到配置文件themedemo.properties中的,默认情况下ResourceBundleThemeSource使用空的前缀名称,所以,classpath根目录下的properties文件会被载入。如果想制定位置,则可以使用basenamePrefix
。
找到主题文件后,如何去解析?此时就出现了ThemeResolver。
SpringMVC中有实现主题类有3个:
-
FixedThemeResolver(默认):固定格式的theme,不能在系统运行时动态更改theme。
-
SessionThemeResolver:可在运行中通过更改cookie中的相应的key值来动态调整theme的值。
-
CookieThemeResolver:可在运行中通过更改session中的相应的key值来动态调整theme的值
主题加载策略和initLocaleResolver类似,也是如果用户没有自定义就采用默认的方式。默认的方式为FixedThemeResolver。
一个例子:
application context .xml配置文件:
?
1 2 3 4 5 6 | <bean class = "org.springframework.ui.context.support.ResourceBundleThemeSource" id= "themeSource" > <property name= "basenamePrefix" value= "themes/" ></property> </bean> <bean id= "themeResolver" class = "org.springframework.web.servlet.theme.SessionThemeResolver" > <property name= "defaultThemeName" value= "red" /> </bean> |
简单叙述为:ThemeResolver找到themes/目录下的所有properties中,然后SessionThemeResolver在发生改变主题请求后来解析主题。
附:如果需要根据用户请求来改变主题,则需要使用ThemeChangeInterceptor拦截器来改变主题。(拦截器有关的知识可以看本文下面的handlerMapping)