DispatcherServlet
Spring MVC,像许多其他web框架,围绕前端控制器模式来设计, 具备一个中央Servlet,DispatcherServlet,它提供了一个 共享的请求处理算法, 而实际工作由可配置的委托组件执行。 这个模型非常灵活,支持多种多样的工作流。
DispatcherServlet像任何Servlet一样,需要根据Servlet规范声明和映射。 可使用Java Config配置或者在web.xml中配置
反过来,DispatcherServlet 使用Spring 配置来发现委托组件来进行请求映射 视图解析,异常处理等等
下面是用于注册和初始化 DispatcherServlet 的 Java 配置的示例。此类由 Servlet 容器自动检测 (请参见 servlet 配置):
public class MyWebApplicationInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletCxt) {
// 加载 Spring web application 配置
AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContex(;
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/*");
}
}
除了直接使用 ServletContext API 外, 还可以扩展 AbstractAnnotationConfigDispatcherServletInitializer 和重写特定方法 (请参见 Context Hierarchy示例)。
AbstractAnnotationConfigDispatcherServletInitializer 是由Spring实现的WebApplicationInitializer 省去像以上手动写一个MyWebApplicationInitializer的诸多麻烦
下面是一个注册和初始化DispatcherServlet的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/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>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
</web-app>
Spring boot遵循不同的初始化序列,它不挂钩到Servlet 容器的生命周期 Spring boot使用Spring 配置去启动它自身和内置Servlet容器 在Spring配置中检测到Filter和Servlet并在Servlet容器中注册 更多详情请查看 Spring boot 文档
Context 层级
DispatcherServlet 期望一个WebApplicationContext —— 一个ApplicationContext的扩展来完成它自身的配置
WebApplicationContext拥有一个指向ServletContext的连接,它与Servlet相关联
它也绑定到ServlerContext,这样如果需要访问它,应用程序可以使用RequestContextUtils 的静态方法来查找WebApplicationContext
对于很多应用来说,拥有单独一个WebApplicationContext是简单的而且也足够。但也可以有层级结构,一个根WebApplicationContext被多个DispatherServlet或其他任何Servlet共享,而这些Servlet又拥有自己特有的WebApplicationContext 见 Additional Capabilities of the ApplicationContext 看更多Context层级功能
典型地,根WebApplicationContext会包含需要在多个Servlet实例中共享的,象data repositories和业务 service的基础设施bean
那些bena将被有效地继承,并可以被重载(即重定义),于具体Servlet,而子WebApplicationContext往往包含给予本地Servlet的Beans
也就是说,root WebApplicationContext是属于应用程序的,而子WebApplicationContext是属于单个Servlet的,Spring优先在子WebApplicationContext中查找bean,如果查找不到,才会在根WebApplicationContext中查找,因此,子WebApplicationContext是可以覆盖父bean配置的
下面是带有WebApplicationContext层级的示例配置
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/*" };
}
}
如果对一个应用程序context层级不是必要的,应用程序可以直接在getRootConfigClasses()中返回所有配置,并从getServletConfigClasses()中返回null
<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>
<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>
如果对一个应用程序context层级不是必要的,应用程序可以只配置一个"root"context并让Servlet 参数contextConfigLocation 为空
特殊的Bean类型
DispatcherServlet委托特殊Bean去处理请求和渲染合适的response,"特殊 beans",意味着是被Spring管理的实现了WebFlux框架约定的对象实例。他们总是随内建约定而来,但你可以配置他们的属性,继承或者替换他们
意思是这些特殊类型的Bean,即使你不手动配置,Spring也会配置好这些它们,但你可以但你可以直接配置Bean,继承或者替换他们
下面的表格列出被DispatcherHandler发现的特殊Bean
Bean类型 | 解析 |
---|---|
HandlerMapping | 映射一个请求到一个Handler以及前置与后置拦截器列表,映射过程基于某些标准,但具体的HandlerMapping实现可以是不同的。主要的实现有支持@RequestMapping注解方法的RequestMappingHandlerMapping和使用明确URI路径模式到Handler的注册的SimpleUrlHandlerMapping |
HandlerAdapter | 帮助DispatcherServlet去调用一个被映射到请求的handler而不用管handler是如何真正被调用的,比如,调用一个注解的控制器需要解析注解,HandlerAdapter的主要意图是对DispatcherServelt屏蔽这样的细节 |
HandlerExceptionResolver | 解析异常的策略,可以能映射它们到handler或者HTML错误视图,或是其它 见Exceptions. |
ViewResolver | 解析从handler返回的基于字符串的逻辑视图名到真正需要渲染到response的视图,见View Resolution 和 View Technologies. |
LocaleResolver, LocaleContextResolver | 解析客户端使用的地区和时区,以提供国际化视图 |
ThemeResolver | 解析你web应用可能使用的主题,比如可以提供个性化的布局 |
MultipartResolver | 解析muti-part 请求(浏览器表单文件上传)的抽象,抽象一些muti-part解析库 |
FlashMapManager | 储存和检索输入和输出的FlashMap,FlashMap可以从一个请求传递属性到另一个请求,通常通过redirect做到 |
Web MVC 配置
应用可以声明特殊Bean类型中列出的处理请求所需的基础设施Bean DispatcherServlet检索WebApplicationContext中的所有特殊Bean 如果没有匹配的Bean类型,它将采用DispatcherServlet.properties中列出的默认类型 在大多数情况下MVC配置是最好的起点,它声明所需Bean于Java或XML中,并提供一个更高级别的配置回调API来定制它
Spring Boot依赖Mvc java Config去配置Spring MVC并提供一些附加的方便的选择
Servlet 配置
在Servlet 3.0+环境中,你可以选择编程式的Servlet容器配置作为代替,或者结合web.xml文件
下面是注册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是SpringMVC提供的一个接口,WebApplicationInitializer的实现能被Servlet3容器自动发现和初始化
一个实现WebApplicationInitializer的基础抽象类AbstractDispatcherServletInitializer能让你通过简单地覆盖方法来详述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也提供了一种方便的方法来添加过滤器,让他们自动映射到DispatcherServlet实例
public class MyWebAppInitializer extends AbstractDispatcherServletInitializer {
// ...
@Override
protected Filter[] getServletFilters() {
return new Filter[] {
new HiddenHttpMethodFilter(), new CharacterEncodingFilter() };
}
}
每个filter将以基于它具体类型的默认名称被加入 并自动映射到DispatcherServlet
在AbstractDispatcherServletInitializer 中的一个名为isAsyncSupported()的protected方法提供一个单独的地方来开启DispatcherServlet的异步支持,并且所有的filter被映射到它,默认为true
处理过程
DispatcherServlet像下面这样处理请求:
WebApplicationContext被搜索并绑定在 像
地区解析器被绑定到请求以允许组件处理请求时去解析地区(渲染视图,准备数据,等等),如果你不需要地区解析,你可以忽略它
主题解析器被绑定到请求,让像视图这样的组件决定使用哪个主题,如果你不使用主题,你可以忽略它
如果你指定一个multipart文件解析器,请求将被作multipart检查,如果发现multipart,请求将被包装在MultipartHttpServletRequest中,以被其它组件进一步处理,见Multipart resolver 查看关于进一步处理的信息
一个合适的handler将被搜索,如果handler被找到,一个连接有handler(前置处理器,后缀处理器,控制器)的执行链将被执行以准备一个model来渲染,对于注解控制器,在HandlerAdapter内部可能返回response而不是view
如果返回了一个view,view将被渲染,否则(可能是因为前置或后置处理器拦截了请求,或是是安全原因),没有view返回,因为请求已经实现了
声明在WebApplicationContext中的HandlerExceptionResolver类型的Bean用来解析在请求处理过程中抛出的一次,那些异常解析器允许自定义逻辑来处理异常,详情见 Exceptions
DispatcherServlet也支持last-modification-date的返回,像用Servlet API 指定一样
你可以通过添加Servlet初始化参数(web.xml中的init-param 元素)定制独特的DispatcherServlet实例,支持的参数见下表
对具体请求,决定最后修改时间的方式是简单粗暴的
DispatcherServlet查找一个合适的handler映射并测试找到的handler是否实现了LastModifed 接口,如果是,long getLastModified(request)方法的返回值将返回给客户端
参数 | 解析 |
---|---|
contextClass | 指定用于这个Servlet的WebApplicationContext的具体实现类,默认使用XmlWebApplicationContext |
contextConfigLocation | 传递给context实例,用来指定context在哪里找到的字符串。这个字符串可以保护多个context,用逗号隔开。如果在多个context中bean被重复定义,那么最后定义的位置优先 |
namespace | WebApplicationContext的名字空间,默认为[servlet名]-servlet |
throwExceptionIfNoHandlerFound | 当找不到请求对应的handler时是否抛出NoHandlerFoundException一次,此异常可以被HandlerExceptionResolver捕获。可通过@ExceptionHandler控制器方法处理,默认为false,这种情况下,DispatcherServlet设置response状态为404而不暴露异常 。注意 如果设置了默认servlet,那么未解析的请求将永远定向到默认servlet而不会暴露404 |
拦截
所有HandlerMapping的实现都支持interceptor,当你需要应用一下特殊的功能到特定请求时,它是有用的
interceptor必须实现org.springframework.web.servlet包中的HandlerInterceptor,它有三个方法,提供了足够的灵活性来做所有前置和后置处理
preHandle() 在真正Handler执行之前执行
postHandle() 在真正Handler执行之后执行
afterCompletion() 在请求完成之后执行
preHandle方法返回一个布尔值,你可以使用这个方法来继续或中断执行链,当返回true时,执行链将继续执行,返回false时DispatcherServlet 假定lnterceptor它自身能处理好请求(如渲染一个合适的视图)而不继续执行执行链中的其它Interceptor和执行真正handler
如何配置interceptor的例子见 interceptors章节, 你也可以直接在具体的 HandlerMapping 实现中用setter注册它
注意在HandlerAdapter 中@ResponseBody和ResponseEntity方法在postHandle之前就将response写入并提交,因此,postHandle对Response的修改 例如添加header,为时已晚。像这样的情况你可以实现ReaponseBodyAdvice并声明为控制器Advice Bean或者在RequestMappingHandlerAdapter中直接配置它
异常
如果一个异常在请求映射中出现或从像@Controller这样的请求Handler中抛出,DispatcherServlet 委托 HandlerExceptionResolver Bean 组成的链 去解析异常并可能提供处理,典型情况是作一个错误Response
下表列出可用的HandlerExceptionResolver实现 SimpleMappingExceptionResolver 一个从异名到错误视图名的映射。在浏览器应用中渲染错误页面是有用的
DefaultHandlerExceptionResolver 解析由SpringMVC引发的异常并映射到Http状态码 见 ResponseEntityExceptionHandler和REST API exceptions
ResponseStatusExceptionResolver
通过异常类上的@ResponseStatus注解来解析异常并根据注解上的状态码映射到Http状态码
ExceptionHandlerExceptionResolver 通过调用@Controller或@ControllerAdvice类中一个@ExceptionHandler方法解析异常 见 @ExceptionHandler methods
Resolver 链
你可以简单地在Spring配置中声明多个HandlerExceptionResolver Bean并按需设置order属性来形成解析器链 order越高的解析器将被定位在越后
HandlerExceptionResolver 返回值的的约定: 指向一个错误页面的ModelAndView 如果异常被解析器处理了,返回空ModelAndView 返回null时,异常保留未解析状态,令后续的异常解析器尝试解析它,如果异常保留未解析状态到达末尾 它将允许被冒泡到Servlet容器
MVC Congif自动地为Spring MVC异常,@ResponseStatus注解异常和 对@ExceptionHandler方法的支持声明内置解析器,。你可以定制列表或者替换它
容器错误页面
如果一个异常由HandlerExceptionResolver保持未解析状态并因此传播出来 或者response状态被视作为错误状态(4xx或5xx) Servlet容器也许渲染一个默认的错误页 你可以在web.xml中声明一个错误页映射
<error-page>
<location>/error</location>
</error-page>
根据上面给出的配置,当一个异常冒泡出来或者respionse有一个错误状态,Servlet容器 将在容器中作一个ERROR分发到上面配置的URL /error。 此后将被DispatcherServlet处理,可能映射到一个@Controller,它可能返回一个错误视图名和模型 或渲染一个JSON Response,如下:
@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;
}
}
Servlet Api 不提供创建错误页面映射的Java方法,但你可以同时使用WebApplicationInitializer和 一个简单的web.xml来实现
视图解析
视图解析一般是将视图名解析到URL而不是解析请求URL Spring MVC 定义一个ViewResolver和View接口,使你可以渲染模型到浏览器而不耦合某一特定的视图技术 ViewResolver提供一个从视图名导真实视图的映射,在将数据交给特定的视图技术之前,View做好数据的准备
下表提供更多ViewResolver层级的细节 AbstractCachingViewResolver ✔
AbstractCachingViewResolver的子类缓存他们解析的视图实例; 缓存提高特定视图技术的性能; 可以通过设置cache属性为false来关闭缓存; 此外,如果你必须在运行时刷新某一确定的视图(比如FreeMarker模版被修改了),你 可以使用removeFromCache(String viewName, Locale loc)方法
XmlViewResolver✔
一个ViewResolver的实现,它接受一个像Spring XML Bean工厂的带DTD的XML配置文件 默认配置文件是/WEB-INF/views.xml
ResourceBundleViewResolver 一个ViewResolver的实现,它使用定义在ResourceBundle中的由bundle的base name指定的Bean, 每一个视图都期望被解析 在property中设置视图名.(class)作为视图类名,注意加括号 而 视图名.url 作为视图路径,不加括号
welcome.(class)=org.springframework.web.servlet.view.JstlView
welcome.url=/WEB-INF/pages/welcome.jsp
例子见View Technologies
UrlBasedViewResolver✔ 一个ViewResolver接口的简单实现,它直接映射逻辑视图名到URL而没有 明确的映射定义,如果你的逻辑名匹配视图资源名是简单粗暴的不需要随意的匹配,这很合适 (可以定义固定的前缀和后缀来映射,它简单地将视图名加上前后缀来映射到本地视图模版路径)
InternalResourceViewResolver✔ 一个UrlBasedViewResolver的简便子类,它支持InternalResourceView(Servlet和JSP) 以及其之类如JstlView和TilesView,你可以通过该Resolver的setViewClass(..)为所有视图指定视图类 详情见UrlBasedViewResolver
FreeMarkerViewResolver✔ UrlBasedViewResolver方便的子类,它支持FreeMarkerView及其子类
ContentNegotiatingViewResolver✔ ViewResolver 接口的实现,它基于请求文件名或Accept Header来解析视图,见Content negotiation.
处理
你可以通过声明一个以上的视图解析bean来链接视图解析器成一条解析链,如果需要 你还可以设置order属性去指定顺序。记住,order越高,view 解析器被定位在链的越后
对于ViewResolver的约定是,它可以返回null表明view未找到。但是在JSP和InternalResourceViewResolver 的情况下,如果JSP存在,唯一的方法是经过RequestDispatcher执行一个分配,因此一个 InternalResourceViewResolver必须总是配置为解析器中的最后顺序 (因为一旦将请求交给Servlet的RequestDispatcher,将无法再返回其它的视图解析流程)
配置视图的方案是简单地添加一个视图解析器Bean到你的Spring配置中 MVC Config为View Resolver和logic-less(轻逻辑)视图控制器提供一个专用的配置API,轻逻辑视图控制器 不带控制器逻辑就可以渲染HTML
重定向
在视图名中特殊前缀redirect:允许你执行一个重定向; UrlBasedViewResolver及其子类会识别这个前缀作重定向; 它后面紧跟着的部分是重定向URL
效果更控制器返回一个RedirectView是一样的,但现在控制器自己能简单地对逻辑视图名操作 一个像redirect:/myapp/some/resource这样的视图名将作相对重定向到当前Servlet Context 而redirect:http://myhost.com/some/arbitrary/pat这样的视图名将作绝对重定向
注意如果一个控制器方法被注解@ResponseStatus,注解值的Response状态将优先于RedirectView
转发
同样可以使用forward:前缀作转发,它最终被UrlBasedViewResolver或其子类解析; 这将创建一个InternalResourceView,它执行RequestDispatcher.forward(); 因此,这个前缀对InternalResourceViewResolver 和 InternalResourceView (for JSPs) 没用; 但它在其它视图技术想强制转发一个资源到Servlet/JSP处理时有用; 注意你也许也链接多个视图解析器
内容导航
ContentNegotiatingViewResolver不自己解析视图,而委托给其它视图解析器,并选择 兼容于客户端请求的表示的视图; 该表示可以从Accept header或者查询次数确定,如/path?format=pdf
ContentNegotiatingViewResolver通过对照请求的media type(s):HTTP的Content-Type和视图解析器 关联的View支持的media type去选择一个合适的视图来处理请求 列表中兼容该Content-Type的第一个View将返回给客户端;
如果ViewResolver链无法提供兼容的视图,将查询DefaultViews属性指定的视图列表; 这一后手适用于单个可以适当地呈现当前资源而不管逻辑视图名如何的视图; Accept header也许包含通配符像text/*,这种情况下Content-Type为text/xml的View是匹配的
配置详情见MVC Config下的View Resolvers
Locale
大部分Spring的结构体系支持国际化,Spring Web MVC也是这样; DispatcherServlet让你使用客户端地区信息可以自动解析消息,这通过LocaleResolver对象来完成; 一但一个请求发进来,DispatcherServlet查找一个locale resolver,如果它找到一个,它将尝试以此来设置;locale 使用RequestContext.getLocale()方法,你可以随时检索被locale resolver解析出来的locale; 在特殊场景下,你也可以通过添加拦截器到HandlerMapping以改变locale,比如基于一个请求参数 Locale和拦截器被定义在org.apringframework.web.servlet.i18n 包中并一般被配置在你的application context中;
下面是你可选择的Spring包含的locale resolver:
TimeZone
取得客户端的locale同时,顺便取得时区是有用的; LocaleContextResolver接口提供一个LocaleResolver的扩展,它允许resolver提供一个更丰富的 LocaleContext,携带时区信息
当这可行时,用户的时区将被RequestContext.getTimeZone()方法取得 时区信息将自动被用到注册于Spirng ConversionService中的Date/Time Converter和Formatter对象
Header
这个locale resolver 检查客户端请求中名为accept-language 的header; 这个header域常常包含客户端的操作系统的locale信息; (因此不能改变locale) 注意这个resolver不支持时区信息;
Cookie resolver
这个locale resolver检查一个可能存在于浏览器的cookie来了解是否指定了一个Locale或TimeZone 如果指定,它使用这些指定的细节; 在此locale resolver中使用properties,你可以指定cookie名和maximun age;
定义一个CookieLocaleResolver的例子如下: (因为是保存在cookie中,你可以改变locale)
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver">
<property name="cookieName" value="clientlanguage"/>
<!-- 如果秒数设置为,cookie将不会被持久化,它将持续到浏览器关闭 -->
<property name="cookieMaxAge" value="100000"/>
</bean>
CookieLocaleResolver properties
Property | Default | Description |
---|---|---|
cookieName | 类名+LOCALE | cookie名 |
cookiePath | / | |
cookieMaxAge | Servlet容器默认 | cookie持久化保存的最大时间,如果是cookie将不持久化,它保持到浏览器关闭 |
Session resolver
SessionLocaleResolver允许你从用户请求关联的Session中检索Locale
SessionLocaleResolver允许你从与用户请求相关的Session中去检索Locale和TimeZone。
相比CookieLocaleResolver,这个策略将本地选择的locale设置在Servlet容器的HttpSession中 那些设置对每个session只是临时的,因此当session关闭时丢失
注意这跟外部session管理机制如Spring Session没有直接关系; 这个SessionLocaleResolver将简单地评估和修改当前HttpServletRequest中合适的HttpSession属性
Locale interceptor
你通过添加LocaleChangeInterceptor到一个handler mapping来启用locale改变; 它将发现一个请求中的参数并改变locale。 他调用context中存在的LocaleResolver的setLocale() 后面的示例展现 如 http://www.sf.net/home.view?siteLanguage=nl 将改变网站语言为荷兰语;
现在所有包含一个名为siteLanguage的参数的对*.view资源的调用,将改变locale
<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>
主题
你可以应用Spring web mvc框架主题来设置你应用的整体视感,亦即增强用户体验; 一个主题是一个静态资源集合,典型的是影响你应用的css样式表和图片
定义一个主题
要在你到web应用中使用主题,你必须设置一个 org.springframework.ui.context.ThemeSource接口的实现 WebApplicationContext接口继承了ThemeSource但它将责职委托到专门的实现 默认的委托是一个 org.springframework.ui.context.support.ResourceBundleThemeSource 实现, 它从classpath根目录加载properties文件。
要使用一个自定义ThemeSource实现或者配置ResourceBundleThemSource 的base name 前缀,你可以注册一个bean到 applicationContext中,命名为 themeSource web application context 自动发现此名的bean并使用它
当使用ResourceBundleThemeSource时,主题应在一个简单的properties文件中定义;
properties文件列出组成主题的资源,下面是示例:
styleSheet=/themes/cool/style.css background=/themes/cool/img/coolBg.jpg
properties中的key将是在view代码中引用主题元素的名称; 对于JSP你可以使用spring:theme自定义标签,它非常类似于spring:message标签; 后面的JSP片段使用前面例子中定义的主题来定制观感;
<%@ 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使用一个空的base name前缀。 导致properties文件将从classpath中加载。 如果你想放一个cool.properties主题定义到一个classpath的根目录下 像/WEB-INF/classes. ResourceBundleThemeSource使用标准java资源集加载机制允许全部主题的国际化; 比如,我们可以有一个/WEB-INF/classes/cool_nl.properties,它引用一个带荷兰文字的 特殊的背景图片;
解析主题
当你定义了一个主题,如前面的章节,你决定使用哪个主题; DispatcherServlet将查找名为themeResolver的bean去寻找ThemeResolver的实现来使用; 一个主题解析器工作方法很像LocaleResolver; 它对具体的请求寻找主题来使用,也能修改请求的主题;
下面的theme resolver由Spring提供
FixedThemeResolver
选择使用defaultThemeName属性设置的固定主题
SessionThemeResolver
主题保存在用户的HTTP Session中,只需要为每个session设置一次,当不会在session中持久化
CookieThemeResolver
选择的主题储存在客户端的cookie中
Spring 也提供了hemeChangeInterceptor,它允许通过简单的请求参数来修改每个请求的主题
Multipart resolver
org.springframework.web.multipart包中的MultipartResolver是一种解析包含文件上传的multipart 请求的策略; 有一种实现基于Commons FileUpload; 而另一种实现基于Servlet 3.0 multipart 请求解析
你需要在你的DiapatherServlet Spring配置名为multipartResolver(小写开头)的MultipartResolver Bean来启用multiparr处理; DispatcherServlet发现它并将它应用到发进来的请求
当接收到一个带有"multipart/form-data"的Content-Type的POST时,resolver解析内容并包装当前 HttpServletRequest为MultipartHttpSeevletRequest以提供入口去解析并将它们作为request parameters暴露;
Apache FileUpload
简单地配置一个类型为名为multiparrResolver的CommonsMultipartResolver就可以使用Apache Common FileUpload。当然你的classpath中要有common-fileupload的依赖;
Servlet 3.0
Servlet 3.0 multipart 解析需要通过Servlet容器配置来实现;
以Java方式,可以设置一个 MultipartConfigElement到Servlet registration来实现.
以web.xml方式, 可以添加一个"<multipart-config>"的XML Element到Servlet声明来实现
public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
// ...
@Override
protected void customizeRegistration(ServletRegistration.Dynamic registration) {
// Optionally also set maxFileSize, maxRequestSize, fileSizeThreshold
registration.setMultipartConfig(new MultipartConfigElement("/tmp"));
}
}
一旦Servlet 3.0配置好,简单地添加一个类型为StandardServletMultipartResolver,名为 multipartResolver的Bean即可;
这一章节内容很丰富,水平有限,诸多错误,不流畅,不够透彻的地方将在后续更新中订正,还望各位大佬不吝赐教
接下来将开始下一章的旅程
第一章完结,撒花