Spring MVC 解析之 DispatcherServlet

Spring MVC 是什么

Spring Web MVC (Spring MVC) 是一套以 Servlet API 为基础平台的优雅的 Web 框架,一直是 Spring Framework 中重要的一个组成部分。 正式名称 “Spring Web MVC” 来自其源模块 spring-webmvc 的名称,但它一般被称为“Spring MVC”。css

与 Spring Web MVC 并行,Spring Framework 5.0 引入了一个 Reactive stack —— Web框架,其名称 Spring WebFlux 也基于它的源模块 spring-webflux。html

DispatcherServlet

与许多其余 Web 框架同样,Spring MVC 一样围绕前端页面的控制器模式 (Controller) 进行设计,其中最为核心的 Servlet —— DispatcherServlet 为来自客户端的请求处理提供通用的方法,而实际的工做交由可自定义配置的组件来执行。 这种模型使用方式很是灵活,能够知足多样化的项目需求。前端

和任何普通的 Servlet 同样,DispatcherServlet 须要根据 Servlet 规范使用 Java 代码配置或在 web.xml 文件中声明请求和 Servlet 的映射关系。 DispatcherServlet 经过读取 Spring 的配置来发现它在请求映射,视图解析,异常处理等方面所依赖的组件。java

如下是注册和初始化 DispatcherServlet 的 Java 代码配置示例。 该类将被 Servlet 容器自动检测到:web

public class MyWebApplicationInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletCxt) {

        // 加载 Spring Web Application 的配置
        AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
        ac.register(AppConfig.class);
        ac.refresh();

        // 建立并注册 DispatcherServlet
        DispatcherServlet servlet = new DispatcherServlet(ac);
        ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet);
        registration.setLoadOnStartup(1);
        registration.addMapping("/app/*");
    }
}
复制代码

如下是在 web.xml 中注册和初始化 DispatcherServlet 的方法:spring

<web-app>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <!-- Spring 上下文的配置 -->
        <param-value>/WEB-INF/app-context.xml</param-value>
    </context-param>

    <servlet>
        <servlet-name>app</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value></param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>app</servlet-name>
        <!-- "/*" 表示将全部请求交由 DispatcherServlet 处理 -->
        <url-pattern>/*</url-pattern>
    </servlet-mapping>

</web-app>
复制代码

1. 应用上下文的层次结构

DispatcherServlet 依赖于一个 WebApplicationContext(对普通 ApplicationContext 的功能扩展)来实现本身的配置。 WebApplicationContext 中包含了一个指向它所关联的 ServletContext 和 Servlet 的连接。 它同时还绑定到 Servlet 上下文中,以便应用程序可使用 RequestContextUtils 中的静态方法在 WebApplicationContext 进行查找,来判断是否须要调用 DispatcherServlet 中的方法。浏览器

对于只有一个 WebApplicationContext 应用程序来讲,这已经能够知足使用了。 同时也可使用具备层次结构的上下文,其中有一个根上下文(或者叫基上下文) 被多个 DispatcherServlet(或其余普通 Servlet)实例所共享,每一个实例都有属于本身的子上下文配置。缓存

根上下文一般包含被多个 Servlet 实例共享的公共 bean,例如数据仓库和业务。 这些 bean 被继承下来使用,还能够在特定的 Servlet 的子上下文中重写(即从新声明一个 bean 的配置),子上下文中拥有该 Servlet 所独有的局部 bean 实例:安全

如下是使用 WebApplicationContext 层次结构的示例配置:cookie

public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[] { RootConfig.class };
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[] { App1Config.class };
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/app1/*" };
    }
}
复制代码

在 web.xml 中的配置

<web-app>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <!-- 根上下文的配置 -->
        <param-value>/WEB-INF/root-context.xml</param-value>
    </context-param>

    <servlet>
        <servlet-name>app1</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <!-- app1 专属的的子上下文 -->
            <param-value>/WEB-INF/app1-context.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>app1</servlet-name>
        <url-pattern>/app1/*</url-pattern>
    </servlet-mapping>

</web-app>
复制代码

2. 带有特殊功能的 Bean

DispatcherServlet 依靠这些特殊的 bean 来处理请求并返回响应。 这些特殊的 bean 是指实现 WebFlux 框架协议的,一样由 Spring 管理的对象。 这些对象都含有一套默认的配置,但也能够自定义各类属性,从而进行灵活扩展或功能改写。

bean 类名 功能
HandlerMapping 将请求映射处处理程序以及用于预处理和后续处理的一系列拦截器。 这种映射有着一套标准,具体的功能因 HandlerMapping 实现而异。 HandlerMapping 的两个最主要实现是 RequestMappingHandlerMapping 和 SimpleUrlHandlerMapping ,前者支持 @RequestMapping 注释方法,它为请求的处理进行 URI 映射的注册。
HandlerAdapter 协助 DispatcherServlet 调用匹配请求映射的处理程序,且不须要关心如何调用处理程序以及处理程序的任何细节。 例如,调用带注释的控制器中的方法须要先对 @RequestMapping 等注释进行解析。 HandlerAdapter 的主要功能是屏蔽 DispatcherServlet 的实现细节。
HandlerExceptionResolver 包含各类异常的解决方法,能够将不一样的异常映射到响应的处理程序或页面等。
ViewResolver 将处理程序中的方法返回值(字符串)的逻辑视图名称解析为实际视图,来将响应返回给客户端。
LocaleResolver, LocaleContextResolver 识别客户端的当前区域设置以估测大概的时区,从而可以返回响应地区的国际化视图。
ThemeResolver 解析当前 Web 应用程序可用的主题,例如提供个性化布局。
MultipartResolver 在相应的解析库的辅助下,对 multi-part 请求(好比浏览器的表单文件上传)进行解析。
FlashMapManager 存储和检索可将参数从一个请求传递到另外一个请求的“输入”和“输出”的 FlashMap,一般经过重定向来实现。

3. Web MVC 的配置

应用程序能够单独声明上文中“带有特殊功能的 Bean”中列出的基础版的 bean。 DispatcherServlet 扫描这些 bean 所属的 WebApplicationContext。 若是没有匹配的 bean 类型,它将返回 DispatcherServlet.properties 中的默认类型。

在大多数状况下,MVC 默认配置是最好的实现。 它采用 Java 代码或 XML 文件来配置所需的 bean,同时提供更高级别的配置回调 API 用于改写默认配置。

4. Servlet 的配置

在 Servlet 3.0 以上的版本中,能够用 Java代码或与 web.xml 文件相结合来配置 Servlet 容器。 如下是用 Java 代码注册 DispatcherServlet 的示例:

import org.springframework.web.WebApplicationInitializer;

public class MyWebApplicationInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext container) {
        XmlWebApplicationContext appContext = new XmlWebApplicationContext();
        appContext.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml");

        ServletRegistration.Dynamic registration = container.addServlet("dispatcher", new DispatcherServlet(appContext));
        registration.setLoadOnStartup(1);
        registration.addMapping("/");
    }
}
复制代码

WebApplicationInitializer 是 Spring MVC 提供的一个接口,其全部实现类均可以被扫描到,并自动用于初始化任何 Servlet 3 容器。 AbstractDispatcherServletInitializer (WebApplicationInitializer 的一个抽象父类)的实现类能够经过覆盖方法来配置 Servlet 请求映射、DispatcherServlet 配置文件的目录,这样很简单的就实现了 DispatcherServlet 的配置。

若是经过 Java 代码来配置 Spring 的话,须要这样作:

public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return null;
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[] { MyWebConfig.class };
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }
}
复制代码

若是用 xml 文件来配置 Spring,则只需定义一个 AbstractDispatcherServletInitializer 实现类便可:

public class MyWebAppInitializer extends AbstractDispatcherServletInitializer {

    @Override
    protected WebApplicationContext createRootApplicationContext() {
        return null;
    }

    @Override
    protected WebApplicationContext createServletApplicationContext() {
        XmlWebApplicationContext cxt = new XmlWebApplicationContext();
        cxt.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml");
        return cxt;
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }
}
复制代码

AbstractDispatcherServletInitializer 还能够轻松添加 Filter 并自动映射到 DispatcherServlet 中:

public class MyWebAppInitializer extends AbstractDispatcherServletInitializer {

    // ...

    @Override
    protected Filter[] getServletFilters() {
        return new Filter[] {
            new HiddenHttpMethodFilter(), new CharacterEncodingFilter() };
    }
}
复制代码

不一样的 Filter 都会以不一样的名称注册,同时映射到 DispatcherServlet 中。

The isAsyncSupported protected method of AbstractDispatcherServletInitializer provides a single place to enable async support on the DispatcherServlet and all filters mapped to it. By default this flag is set to true.

AbstractDispatcherServletInitializer 中的 isAsyncSupported() 方法能够设置各个 Filter 是否开启异步支持。

若是还想更加细化自定义配置,能够经过重写 createDispatcherServlet() 方法来实现。

5. 处理请求

DispatcherServlet 处理请求的规则:

  • 在请求中查找并绑定 WebApplicationContext,它能够做为参数被控制器中的方法使用。 默认绑定到 DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE 对应的值。
  • 区域解析器 (LocaleResolver) 也绑定到请求上,它能够在请求解析、呈现视图、准备数据等过程当中将信息解析为当前的区域环境。若是无需解析这些信息,能够不用管它。
  • 主题解析器用来决定使用哪一个主题。 若是你不使用主题,能够忽略掉它。
  • 若是在应用中声明了 multipart file resolver,则会对请求进行 multipart 检查;若是发现了 multiparts,请求会被包装成 MultipartHttpServlet 来进行处理。
  • 若是返回模型,则会解析并返回视图。 若是没有返回模型(因为其余处理程序拦截了请求,可能出于安全缘由),则不会返回视图,由于可能已经有响应返回给客户端了。

WebApplicationContext 中声明的 HandlerExceptionResolver bean 能够解析请求处理时抛出的异常。 能够给异常解析器进行特定的配置来解决特定的异常。

DispatcherServlet 还支持返回最后修改日期。 DispatcherServlet 扫描注册的映射关系并,判断找到的处理程序是否实现了 LastModified 接口。 若是实现了,则将 LastModified 接口的 long getLastModified(request)方法的返回值返回给客户端。

在 web.xml 中,能够经过配置 Servlet 的初始化参数(init-param)来自定义一个 DispatcherServlet 的实例。

DispatcherServlet 的初始化参数

参数 含义
contextClass WebApplicationContext 的实现类,初始化 Servlet 的上下文,默认为 XmlWebApplicationContext
contextConfigLocation 做为参数传递给在 contextClass 中指定上下文实例,用于标识上下文的位置,接受多个字符串(以逗号分隔),若是同一个 bean 的配置在多个上下文中出现,则以最后一个为准。
namespace WebApplicationContext 的命名空间,默认为 元素中的值加上“servlet”。好比,app,那么,命名空间为 appServlet。

6. 拦截器

全部 HandlerMapping 的实现类都支持使用拦截器,特别是将某些功能只应用到特定的请求上时(好比判断权限),拦截器就很是有用。 拦截器必须实现 org.springframework.web.servlet 包中的 HandlerInterceptor,它提供了三个方法,供不一样时刻来调用:

  • preHandle(..):在请求处理前执行...
  • postHandle(..):在请求处理后执行...
  • afterCompletion(..):在请求处理彻底结束后执行...

preHandle(..) 方法返回布尔值,能够经过这一点选择来切断或继续请求的处理链。当返回 true 的时候,处理器链会继续执行;返回 false 的时候, DispatcherServlet 就会认为拦截器已经处理了请求或返回了视图,并不会继续被处理链中的其余处理器或拦截器所处理。

请注意,postHandle(..) 对使用 @ResponseBody 和 ResponseEntity 方法的用处不大,在 HandlerAdapter 中, postHandle(..) 调用以前就已经提交响应了。 因此这时再修改响应什么用也没有了。 这种状况下,能够实现 ResponseBodyAdvice,并将其声明为 Controller Advice bean (在 Controller 类上添加 @ControllerAdvice 注解)或直接在 RequestMappingHandlerAdapter 中进行配置。

7. 异常处理

在映射或调用请求处理程序 (例如带有 @Controller 注解的控制器) 处理请求时,若是抛出了异常,则 DispatcherServlet 调用 HandlerExceptionResolver bean 来处理异常,而后将错误页面或错误状态码等信息返回个客户端。

下面列出 HandlerExceptionResolver 的几个实现类:

HandlerExceptionResolver 实现类

类名 功能
SimpleMappingExceptionResolver 将异常类型映射到异常页面的视图名,能够很容易实现错误页面的返回
DefaultHandlerExceptionResolver 处理由 Spring MVC 抛出的异常,能够直接映射到不一样的 HTTP 状态码
ResponseStatusExceptionResolver 处理带有 @ResponseStatus 注解的异常,并将其映射到 HTTP 状态码
ExceptionHandlerExceptionResolver 经过调用控制器(带有 @Controller 注解或 @ControllerAdvice 注解)中的带有 @ExceptionHandler 注解的方法,来处理异常

若是须要同时映射多个异常类型,须要设置不一样异常的权重 (order 属性),权重越高,处理时机越晚。

处理方式

HandlerExceptionResolver 中能够返回:

  • ModelAndView: 指向视图名
  • 不带属性的 ModelAndView: 若是已经经过权重更低的方法处理过异常了
  • null: 这个异常没法被当前异常处理器识别,须要丢给接下来的处理器,若是全部处理器都不能处理这个异常,异常会传到 Servlet 容器中,由容器来处理
  • 自定义异常处理请求也很是简单,好比,在 xml 中配置一个 HandlerExceptionResolver 的 bean, Spring MVC 会自动使用内部的默认异常处理器来处理 Spring MVC 抛出的异常(带有 @ResponseStatus 注解的异常或带有 @ExceptionHandler 注解的方法),能够修改这些默认配置或干脆直接重写新的配置。

Servlet 容器中定义的错误页面 (error-page)

若是抛出的异常没能被任何 HandlerExceptionResolver 处理,就会传到 Servlet 容器中。若是将 HTTP 响应码设置为 4xx 或 5xx, Servlet 容器会直接返回默认的错误页面。能够在 web.xml 中配置自定义的错误页面:

<error-page>
    <location>/error</location>
</error-page>
复制代码

此时,若是想要对异常处理进一步自定义,能够这样作:

@RestController
public class ErrorController {

    @RequestMapping(path = "/error")
    public Map<String, Object> handle(HttpServletRequest request) {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("status", request.getAttribute("javax.servlet.error.status_code"));
        map.put("reason", request.getAttribute("javax.servlet.error.message"));
        return map;
    }
}
复制代码

这样就能再次将 /error 请求交给 DispatcherServlet 来处理,这种方式解决了使用 RestController 返回 JSON,而不是返回视图的状况。

8. 视图名解析

Spring MVC 定义了 View 和 ViewResolver 这两个接口,View 负责返回视图前的数据准备, ViewResolver 则负责将逻辑视图名映射到实际视图。 屏蔽了具体的视图实现细节。

ViewResolver 的几个实现类

类名 功能
AbstractCachingViewResolver AbstractCachingViewResolver 的实现类会将解析过的视图缓存,缓存能够提高特定视图技术的性能。能够经过设置 cache 属性的值为 false 来将缓存功能关闭。若是想在运行时刷新视图缓存,能够调用 removeFromCache(String viewName, Locale loc) 方法将已缓存内容移除。
XmlViewResolver 可使用 Spring bean DTD 定义,在 xml 中对其配置。默认的配置文件为 /WEB-INF/views.xml
ResourceBundleViewResolver 经过解析定义的 ResourceBundle bean,将 viewname 解析为视图名,url 解析为视图路径
UrlBasedViewResolver 能够直接将 url 映射到返回值中,无需再进行额外的映射配置,适用于视图结构清晰,能够直接对应匹配的状况
InternalResourceViewResolver UrlBasedViewResolver 的子类,能够解析 InternalResourceView (好比 Servlet 或 JSP) 以及其子类,好比 JstlView 或 TilesView。能够经过 setViewClass(..) 方法来指定要解析的具体视图类型
FreeMarkerViewResolver UrlBasedViewResolver 的子类,能够解析 FreeMarkerView 以及自定义的子类
ContentNegotiatingViewResolver 经过请求的 url 或请求头信息来解析视图

处理方式

和上面提到的异常解析器同样,经过配置 bean 来解析视图,一样能够指定不一样解析器的权重,权重越高,调用时机越晚。

ViewResolver 能够经过返回 null 来表示视图没法解析,若是是 JSP 或 InternalResourceViewResolver,能够判断 JSP 是否存在的惟一方法就是经过执行 RequestDispatcher 的请求调度方法。 所以必须将 InternalResourceViewResolver 的权重配置为全部视图解析器中最高的。

若是返回视图的执行过程不须要处理任何的业务逻辑,可使用视图控制器来实现:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        // 将 url 为 "/" 的请求映射到名为 home 的视图上
        registry.addViewController("/").setViewName("index");
    }
}
复制代码

在 xml 中这样写:

<mvc:view-controller path="/" view-name="index"/>
复制代码

重定向

若是返回的视图名以 "redirect:" 开头, UrlBasedViewResolver 就会将其解析为请求重定向,后面的视图名就是将重定向的 url。

这样和返回 RedirectView 的效果是同样的,他能够将请求重定向到当前 Servlet 上下文的相对路径,若是写成这样 redirect:http://myhost.com/some/arbitrary/path,就会重定向到这个绝对路径上。

若是在控制器的方法上写了 @ResponseStatus 注解,则注解中的状态码优先权高于 RedirectView 中的。

转发

若是最终的视图解析器为 UrlBasedViewResolver, 则可使用 forward:,效果经过 forward() 方法建立一个 InternalResourceView 。这个前缀不适用于 InternalResourceViewResolver 和 InternalResourceView (JSP),但对于使用其余的视图技术且想执行请求转发,就很是有用了。一样,将多个视图解析器组成处理链实现链式处理。

内容规约

ContentNegotiatingViewResolver 实际并不会解析视图,而是调用其余视图解析器,查找与客户端请求中有关信息匹配的视图。这些信息能够从请求头中的 Accept 中或 url 中参数得到。

ContentNegotiatingViewResolver 将请求的媒体类型与 ViewResolvers 关联的 View 支持的媒体类型(也称为 Content-Type)进行比较,选择最佳的 View 来处理请求。 与这个 Content-Type 相符的第一个视图会被首先返回给客户端。 若是 ViewResolver 链没法解析视图,则会在 DefaultViews 的视图列表进行查找。 后者适用于无需进行业务逻辑处理的单例 View ,所以不用考虑逻辑视图名。 Accept 头中能够包含通配符,例如 text/ * ,则将匹配 content-type 为 text/xml的 View 。

9. 语言环境

Spring MVC 和 Spring 中的大多数模块同样,也提供了内容国际化的功能。DispatcherServlet 能够根据客户端的语言环境进行内容的自动国际化,这要归功于 LocaleResolver。

DispatcherServlet 接受到请求后会去上下文中查找 LocaleResolver bean,找到后就会使用它完成国际化的任务。能够调用 RequestContext.getLocale() 来得到请求的语言环境。

若是不想使用默认的自动解析方式,还能够经过在处理程序映射中添加一个拦截器来实现,好比解析 url 中的参数。

有关国际化的解析器和拦截器都定义在 org.springframework.web.servlet.i18n 中,而且都须要在应用上下文中配置。Spring 能够国际化这些内容:

时区

除了语言环境,有时还须要得到客户端的时区信息,LocaleContextResolver 对 LocaleResolver 中的功能进行了扩展,这样能够在 LocaleContext 中得到更为丰富的信息。

经过 RequestContext.getTimeZone()方法获取客户端的时区信息,在 Spring 的 ConversionService 中注册的日期/时间转换器和格式化器会自动获取这些信息。

请求头

这个区域解析器会识别请求中的 accept-language 头,这个字段通常包含客户操做系统的区域信息,可是它没法得到客户端的时区信息。

Cookie

这个区域解析器会检查客户端上的 Cookie 是否存在语言或时区信息。这个区域解析器能够设置 cookie 的名字和失效时间。在 xml 文件中定义 CookieLocaleResolver:

<bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver">

    <!-- 根据具体状况自定义 -->
    <property name="cookieName" value="clientlanguage"/>

    <!-- 100000 为秒数,值为 -1 时, cookie 会在浏览器关闭时被销毁 -->
    <property name="cookieMaxAge" value="100000"/>

</bean>
复制代码

CookieLocaleResolver 部分属性的含义

属性 默认值 含义
cookieName 类名+语言环境 cookie 的名称
cookieMaxAge Servlet 容器中规定的值 cookie 的有效期,值为 -1 时, cookie 会在浏览器关闭时被销毁
cookiePath / 将 cookie 的做用域限制到制定的相对路径下,cookie 将只在这个路径及其自路径中有效

Session

SessionLocaleResolver 能够从会话上下文中获取语言和时区信息,它和 CookieLocaleResolver 的区别是:它是在 Servlet 容器的 session 中存储语言和时区信息,全部,这些信息都是暂时的,会话结束后也就不复存在了。

语言环境拦截器

LocaleChangeInterceptor bean 能够经过自定义配置来修改请求中的参数,达到修改语言环境的目的。调用 LocaleResolver 中的 setLocal() 方法便可。下面的例子演示将全部请求中的 siteLanguage 参数修改成荷兰语:

<bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
    <property name="paramName" value="siteLanguage"/>
</bean>

<bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver"/>

<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <property name="interceptors">
        <list>
            <ref bean="localeChangeInterceptor"/>
        </list>
    </property>
    <property name="mappings">
        <value>/**/*.view=someController</value>
    </property>
</bean>
复制代码

10. 主题

Spring Web MVC 框架支持自定义主题,应用程序的总体外观能够实现统一修改。主题即静态资源的集合,一般是 CSS 和图片,二者决定了应用的总体风格。

主题的定义

必须定义一个 org.springframework.ui.context.ThemeSource 接口的实现类才能使用主题功能。默认状况下,WebApplicationContext 经过 org.springframework.ui.context.support.ResourceBundleThemeSource 实现主题功能,它从 classpath 的根目录读取配置文件。 要使用自定义的 ThemeSource,须要配置 ResourceBundleThemeSource 的主题名称前缀,在应用程序上下文中注册一个 themeSource bean。

使用 ResourceBundleThemeSource 自定义主题,须要将配置写在 properties 文件中,这个文件中包含了构成主题的全部资源。

styleSheet=/themes/cool/style.css
background=/themes/cool/img/coolBg.jpg
复制代码

在 JSP 中使用主题设置,要用到 spring:theme 标签:

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<html>
    <head>
        <link rel="stylesheet" href="<spring:theme code='styleSheet'/>" type="text/css"/>
    </head>
    <body style="background=<spring:theme code='background'/>">
        ...
    </body>
</html>
复制代码

默认状况下,ResourceBundleThemeSource 主题名前缀是空的。properties 文件是从 classpath 的根目录读取的。因此应当将 abc.properties 配置文件放到根目录下(/WEB-INF/classes)。 ResourceBundleThemeSource 能够实现主题的国际化。好比,/WEB-INF/classes/abc_nl.properties,结合上面对 background 属性的配置,能够实现将背景切换为带有荷兰文字的背景图片。

主题的解析

定义好主题配置后,当有请求发来时,在预处理阶段,经过查找上下文中叫 themeResolver 的 bean 来对决定用什么解析器来解析主题,这里的工做原理和上文中的 localeResolver 同样。经过识别带有不一样参数的请求来对请求的主题进行切换。Spring 提供了这几种 themeResolver:

类名 功能
FixedThemeResolver 经过读取 defaultThemeName 属性来选择固定的主题
SessionThemeResolver 主题信息由 session 保存,每一个 session 只需解析一次,不一样的 session 之间没法共享
FixedThemeResolver 主题信息保存在客户端的 cookie 中

Spring 还提供了一个 ThemeChangeInterceptor 拦截器,经过识别请求的参数来切换主题。

11. Multipart

org.springframework.web.multipart.MultipartResolver 提供了一种处理表单上传文件的解决方案,有两种实现方式,一种是基于 Apache 的 Commons-Fileupload,另外一种是基于 Servlet 3.0 的。

首先要在 Spring 的配置文件中为 DispatcherServlet 声明一个叫作 MultipartResolver 的 bean,DispatcherServlet 会自动识别并调用它来处理文件上传请求。它会将 content-type 为 multipart/form-data 的请求包装成 MultipartHttpServletRequest,从而将这些 "part" 暴露为请求的一个参数。

Apache FileUpload

要想使用 Apache Commons-Fileupload,须要将 multipartResolver bean 配置为 CommonsMultipartResolver,另外不要忘了添加 commons-fileupload 的依赖。

Servlet 3.0

若是经过 Servlet 3.0 处理 multipart 请求,则一样须要在 DispatcherServlet 中注册。在 Java 代码中配置的话,须要添加一个 MultipartConfigElement;在 xml 文件中配置的话,添加 <multipart-config> 节点。文件大小限制和文件保存位置等选项一样须要这样配置,由于在 Servlet 3.0 之后,不容许 MultipartResolver 这么干了。

Servlet 3.0 配置好后,只须要在 xml 文件中将 multipartResolver hean 配置为 StandartMultipartResolver 便可。

相关文章
相关标签/搜索