Spring Web MVC 是一个基于Servlet API构建的原始框架。 从一开始就包含在Spring框架中。“spring web mvc”的正式名称来自其源模块(spring-webmvc)的名称,但它通常被称为“spring mvc”。
与spring web mvc并行,springframework5.0引入了一个反应式堆栈Web框架,其名称“spring webflux”,也是基于源模块(spring-webflux)的名称。
一、DispatcherServlet
spring mvc和其他许多web框架一样,都是围绕front controller(前端控制器)模式设计的,其中一个中央Servlet DispatcherServlet为请求处理提供了一个共享算法,而实际工作则由可配置的委托组件执行。该模型具有灵活性,支持多种工作流。
DispatcherServlet和任何Servlet一样,都需要使用Java配置或web.xml文件. 反过来,DispatcherServlet使用Spring配置来发现请求映射、视图解析、异常处理等所需的委托组件。
下面的Java配置示例注册并初始化DispatcherServlet,它由Servlet容器自动检测:
public class MyWebApplicationInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletCxt) {
// Load Spring web application configuration
AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
ac.register(AppConfig.class);
ac.refresh();
// Create and register the DispatcherServlet
DispatcherServlet servlet = new DispatcherServlet(ac);
ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet);
registration.setLoadOnStartup(1);
registration.addMapping("/app/*");
}
}
除了直接使用ServletContext API外,还可以扩展AbstractAnnotationConfigDispatcherServletInitializer并重写特定的方法。
以下示例web.xml文件配置注册并初始化DispatcherServlet:
<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>
上下文层次结构
DispatcherServlet 期望一个WebapplicationContext为其自身配置。WebApplicationContext有一个指向ServletContext及其关联的Servlet的链接。它还绑定到ServletContext,以便应用程序在需要访问WebApplicationContext时可以对RequestContextUtils使用静态方法来查找它。
对于许多应用程序来说,只有一个WebApplicationContext是简单且足够的。还可以有一个上下文层次结构,其中一个根WebApplicationContext跨多个DispatcherServlet(或其他Servlet)实例共享,每个实例都有自己的子WebApplicationContext配置。
根WebApplicationContext通常包含基础结构bean,例如需要跨多个Servlet实例共享的数据存储库和业务服务。这些bean被有效地继承,并且可以在特定于Servlet的子WebApplicationContext中被重写(即重新声明),后者通常包含给定Servlet的本地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/*" };
}
}
如果不需要应用程序上下文层次结构,则应用程序可以通过getRootConfigClasses()返回所有配置,从getServletConfigClasses()返回null。
上面配置等价于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> <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>
如果不需要应用程序上下文层次结构,则应用程序可能只配置“根”上下文,并将contextConfigLocation Servlet参数留空。
特殊Bean类型
DispatcherServlet委托给特殊的bean来处理请求并呈现适当的响应。我们所说的“特殊bean”是指实现框架契约的Spring管理对象实例。它们通常带有内置的契约,但你可以自定义它们的属性并扩展或替换它们。
下表列出了DispatcherServlet所到的特殊bean:
HandlerMapping
将请求与拦截器列表一起映射到处理程序,以进行预处理和后处理。映射基于一些条件,这些条件的细节因handler映射实现而异。
两个主要的HandlerMapping实现是RequestMappingHandlerMapping(它支持@RequestMapping带注解的方法)和SimpleUrlHandlerMapping(维护处理程序的URI路径模式的显式注册)。
HandlerAdapter
帮助DispatcherServlet调用映射到请求的处理程序,而不管处理程序实际是如何调用的。例如,调用带注解的控制器需要解析注解。HandlerAdapter的主要目的是屏蔽DispatcherServlet更多细节。
HandlerExceptionResolver
解决异常的策略,可能将它们映射到处理程序、HTML错误视图或其他目标。
ViewResolver
将从处理程序返回的基于逻辑字符串的视图名称解析为要呈现给响应的实际视图。
LocaleResolver, LocaleContextResolver
解析客户机正在使用的区域设置以及可能的时区,以便能够提供国际化的视图。
ThemeResolver
解析web应用程序可以使用的主题,例如,提供个性化的布局。查看主题。
MultipartResolver
使用一些multipart转换工具转换一个multi-part请求,(例如,浏览器表单文件上传)。
FlashMapManager
存储和检索“input”和“output”FlashMap,它们可用于将属性从一个请求传递到另一个请求,通常是通过重定向。
Web MVC配置
应用程序可以声明处理请求所需的特殊Bean类型中列出的基础结构Bean。DispatcherServlet检查每个特殊bean的WebApplicationContext。如果没有匹配的bean类型,则返回中列出的默认类型DispatcherServlet.properties.
在大多数情况下,MVC配置是最好的起点。它用Java或XML声明所需的bean,并提供更高级别的配置回调API来定制它。
Servlet配置
在Servlet3.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提供的一个接口,它确保检测到你的实现并自动用于初始化任何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() };
}
}
每个过滤器都会根据其具体类型添加一个默认名称,并自动映射到DispatcherServlet。
AbstractDispatcherServletInitializer 的isAsyncSupported来启用对DispatcherServlet和所有映射到它的过滤器的异步支持。默认情况下,此标志设置为true。
DispatcherServlet 处理过程
DispatcherServlet处理请求如下:
- WebApplicationContext在请求中使用控制器和进程中其他元素可以使用的属性进行搜索和绑定。默认情况下,它在DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE绑定。
- 语言环境解析程序绑定到请求,以便进程中的元素解析处理请求(呈现视图、准备数据等)时要使用的区域设置。如果不需要区域设置解析,则不需要区域设置解析程序。
- 主题解析器绑定到请求,以让视图等元素确定要使用哪个主题。如果不使用主题,可以忽略它。
- 如果指定multipart 文件解析程序,则会检查请求是否存在multiparts 。如果找到了multiparts ,则请求被包装在MultipartttpservletRequest中,供进程中的其他元素进一步处理。
- 搜索适当的处理程序。如果找到处理程序,则执行与处理程序(预处理器、后处理器和控制器)关联的执行链,以便准备模型或呈现。或者,对于带注解的控制器,可以呈现响应(在HandlerAdapter中),而不是返回视图。
- 如果返回模型,则渲染视图。如果没有返回任何模型(可能是由于预处理器或后处理器拦截了请求,也可能是出于安全原因),则不会呈现任何视图,因为请求可能已经完成。
在WebApplicationContext中声明的HandlerExceptionResolver bean用于解析请求处理期间引发的异常。这些异常解析器允许定制处理异常的逻辑。
DispatcherServlet 还支持返回由Servlet API指定的最后修改日期。确定特定请求的最后修改日期的过程很简单:DispatcherServlet查找适当的处理程序映射,并测试找到的处理程序是否实现LastModified接口。如果是,则将LastModified接口的long getLastModified(request)方法的值返回给客户端。
你可以通过在web.xml的Servlet定义中添加初始化参数(init-param),自定义DispatcherServlet实例
DispatcherServlet 初始化参数:
- contextClass:该类实现ConfigurableWebApplicationContext,将由该Servlet实例化和本地配置。默认情况下,使用XmlWebApplicationContext。
- contextConfigLocation:传递给上下文实例(由contextClass指定)的字符串,以指示可以在何处找到上下文。字符串可能由多个字符串(使用逗号作为分隔符)组成,以支持多个上下文。如果多个上下文位置包含定义了两次的bean,则最新的位置优先。
- namespace:WebApplicationContext的命名空间。默认为[servlet name]-servlet。
- throwExceptionIfNoHandlerFound:在找不到请求的处理程序时是否引发NoHandlerFoundException。然后可以使用HandlerExceptionResolver(例如,通过使用@ExceptionHandler控制器方法)捕获异常,并将其作为任何其他方法进行处理。默认情况下,被设置为false,在这种情况下,DispatcherServlet将响应状态设置为404(NOT_FOUND),而不会引发异常。
注意,如果还配置了默认的servlet处理,那么未解析的请求总是被转发到默认的servlet,并且不会引发404。
拦截器
所有HandlerMapping实现都支持拦截器,当你希望将特定功能应用于某些请求时,这些拦截器非常有用,例如,检查主体。拦截器必须实现org.springframework.web.servlet包的HandlerInterceptor,包含三个方法,这些方法提供了足够的灵活性来执行各种预处理和后处理:
- preHandle(..):在执行实际处理程序之前
- postHandle(..):在执行处理程序之后
- afterCompletion(..):完成请求后
preHandle(..)方法返回一个布尔值。可以使用此方法中断或继续执行链的处理。当此方法返回true时,处理程序执行链将继续。当它返回false时,DispatcherServlet假设拦截器本身已经处理了请求(例如,呈现了一个适当的视图),并且不会继续执行其他拦截器和执行链中的实际处理程序。
请注意,对于@ResponseBody和ResponseEntity方法,postHandle没有那么有用,因为它们在HandlerAdapter和postHandle之前为其编写和提交响应。这意味着对响应进行任何更改(例如添加额外的头)已经太晚了。对于这种情况,你可以实现ResponseBodyAdvice,并将其声明为spring bean,或者直接在RequestMappingHandlerAdapter上配置它。
异常
如果在请求映射过程中发生异常或从请求处理程序(如@Controller)抛出异常,那么DispatcherServlet将委托给HandlerExceptionResolver bean链来解析异常并提供替代处理,这通常是一个错误响应。
下表列出了可用的HandlerExceptionResolver实现:
- SimpleMappingExceptionResolver:异常类名和错误视图名之间的映射。用于在浏览器应用程序中呈现错误页。
- DefaultHandlerExceptionResolver:解决spring mvc引发的异常,并将它们映射到HTTP状态代码。
- ResponseStatusExceptionResolver:使用@ResponseStatus注释解析异常,并根据注释中的值将它们映射到HTTP状态代码。
- ExceptionHandlerExceptionResolver:通过调用@Controller或@ControllerAdvice类中的@ExceptionHandler方法来解决异常。
Resolvers 链
通过在Spring配置中声明多个HandlerExceptionResolver bean并根据需要设置它们的order属性,可以形成一个异常解析器链。order属性越高,异常解决程序的定位就越晚。
HandlerExceptionResolver明确规定可以返回:
- 指向错误视图的ModelAndView
- 如果异常是在解析程序(resolver)中处理的,则为空的ModelAndView。
- 如果异常仍未解决,则为空,供后续解析器(resolver)尝试;如果异常仍在结尾,则允许它冒泡到Servlet容器。
容器错误页
如果任何HandlerExceptionResolver仍然无法解决异常,则需要进行传播,或者如果响应状态设置为错误状态(即4xx、5xx),Servlet容器可以用HTML呈现默认的错误页。若要自定义容器的默认错误页,可以在中声明错误页映射web.xml文件. 下面的示例演示如何执行此操作:
<error-page> <location>/error</location> </error-page>
在前面的示例中,当出现异常或响应具有错误状态时,Servlet容器在容器内对配置的URL进行错误分派(例如/error)。然后由DispatcherServlet处理,可能将其映射到@Controller,可以实现该操作以返回带有模型的错误视图名称或呈现JSON响应,如下例所示:
@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文件.
视图(View)解决方案
spring mvc定义了ViewResolver和视图接口,这些接口允许你在浏览器中呈现模型,而无需将你绑定到特定的视图技术。ViewResolver提供视图名称和实际视图之间的映射。视图处理在移交给特定视图技术之前的数据准备。
下表列出了ViewResolver 的实现类:
- AbstractCachingViewResolver:解析AbstractCachingViewResolver缓存视图实例的子类。缓存可以提高某些视图技术的性能。可以通过将cache属性设置为false来关闭缓存。此外,如果必须在运行时刷新某个视图(例如,在修改FreeMarker模板时),则可以使用removeFromCache(String viewName,Locale loc)方法。
- XmlViewResolver:接受用XML编写的配置文件,其DTD与Spring的xml配置相同。默认配置文件是/WEB-INF/views.xml。
- ResourceBundleViewResolver:使用由bundle基名称指定的ResourceBundle中的bean定义。对于它应该解析的每个视图,它使用属性[viewname].(class)的值作为视图类,将属性[viewname].url的值用作视图url。
- UrlBasedViewResolver:ViewResolver接口的简单实现,该接口将逻辑视图名称直接解析为URL,而无需显式映射定义。如果你的逻辑名称与视图资源的名称以直接的方式匹配,而不需要任意映射,那么这是合适的。
- InternalResourceViewResolver:UrlBasedViewResolver的子类,支持InternalResourceView(实际上是servlet和JSP)和JstlView、TilesView等子类。你可以使用setViewClass(..)为这个解析器生成的所有视图指定视图类。
- FreeMarkerViewResolver:UrlBasedViewResolver的子类,支持FreeMarkerView及其自定义子类。
- ContentNegotiatingViewResolver:基于请求文件名或Accept头解析视图。
处理(Handling)
你可以通过声明多个解析器bean来链接视图解析器,如果需要,还可以通过设置order属性来指定顺序。请记住,order属性越高,视图解析器在链中的位置就越晚。
ViewResolver的协定指定它可以返回null以指示找不到视图。但是,对于JSP和InternalResourceViewResolver,确定JSP是否存在的唯一方法是通过RequestDispatcher执行调度。因此,必须始终将InternalResourceViewResolver配置为视图解析程序总体顺序中的最后一个。
配置视图解析就像在Spring配置中添加ViewResolver bean一样简单。
重定向(Redirecting)
视图名称中的redirect: 前缀允许执行重定向。UrlBasedViewResolver(及其子类)将其识别为需要重定向的指令。视图名称的其余部分是重定向URL。
这个效果与控制器返回了RedirectView相同,但现在控制器本身可以按逻辑视图名称进行操作。逻辑视图名称(例如redirect:/myapp/some/resource)相对于当前Servlet上下文重定向,而像这样的名称重定向:https://myhost.com/some/anytrary/path重定向到绝对URL。
请注意,如果控制器方法用@ResponseStatus注释,则注释值优先于RedirectView设置的响应状态。
转发(Forwarding)
视图名称中的forward: 前缀允许执行转发,UrlBasedViewResolver(及其子类)对它进行识别。这将创建一个InternalResourceView,它执行RequestDispatcher.forward(). 因此,此前缀对于InternalResourceViewResolver和InternalResourceView(对于JSP)没有用处,但是如果你使用另一种视图技术,但仍然希望强制由Servlet/JSP引擎处理资源的转发,则此前缀可能会有帮助。请注意,你也可以链接多个视图解析器。
内容协商(Content Negotiation)
ContentNegotiatingViewResolver本身不解析视图,而是委托给其他视图解析程序并选择与客户端请求的表示类似的视图。表示可以从Accept头或查询参数(如:"/path?format=pdf")
ContentNegotiatingViewResolver通过将请求媒体类型与其每个ViewResolver关联的视图支持的媒体类型(也称为内容类型)进行比较,选择适当的视图来处理请求。列表中具有兼容内容类型的第一个视图将表示形式返回给客户端。如果ViewResolver链无法提供兼容的视图,则会查询通过DefaultViews属性指定的视图列表。后一个选项适用于可以呈现当前资源的适当表示形式的单例视图,而不考虑逻辑视图名称。Accept头可以包含通配符(例如text/*),在这种情况下,内容类型为text/xml的视图是兼容的匹配项。
国际化(Local)
Spring架构的大部分部分都支持国际化,就像spring web mvc框架也一样。DispatcherServlet允许你使用客户端的区域设置自动解析消息。这是通过LocaleResolver对象完成的。
当一个请求进来时,DispatcherServlet会查找一个区域设置解析器,如果找到了,它会尝试使用它来设置区域设置。通过使用RequestContext.getLocale()方法,则始终可以检索由区域设置解析程序解析的区域设置。
除了自动区域设置解析之外,还可以将拦截器附加到处理程序映射以在特定情况下(例如,基于请求中的参数)更改区域设置。
区域解析程序和拦截器在org.springframework.web.servlet.i18n包,并按常规方式在应用程序上下文中配置。Spring中包括以下几种区域设置解析器。
Time Zone
LocaleContextResolver接口提供了对LocaleResolver的扩展,它允许解析器提供更丰富的LocaleContext,其中可能包括时区信息。
用户的时区可用RequestContext.getTimeZone()方法获得,时区信息由注册到Spring转换服务的任何日期/时间转换器和格式化程序对象自动使用。
Header Resolver
此区域设置解析程序检查客户端(例如,web浏览器)发送的请求中的accept language标头。通常,此头字段包含客户端操作系统的区域设置。请注意,此解析程序不支持时区信息。
Cookie Resolver
此区域设置解析程序检查客户端上可能存在的Cookie,以查看是否指定了区域设置或时区。如果是,则使用指定的详细信息。通过使用此区域设置解析程序的属性,可以指定cookie的名称以及最长使用期限。以下示例定义CookieLocaleResolver:
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver"> <property name="cookieName" value="clientlanguage"/> <!-- in seconds. If set to -1, the cookie is not persisted (deleted when browser shuts down) --> <property name="cookieMaxAge" value="100000"/> </bean>
CookieLocaleResolver可配置属性:
- cookieName:cookie的名称,默认是是classname + LOCAL。
- cookieMaxAge:cookie在客户端上持续的最长时间。如果指定了-1,则不会持久化cookie。它仅在客户端关闭浏览器之前可用。默认是Servlet容器的默认值
- cookiePath:将cookie的可见性限制在站点的某个特定部分。指定cookiePath时,cookie仅对该路径及其下面的路径可见。默认是/。
Session Resolver
SessionLocaleResolver允许你从可能与用户请求关联的会话中检索区域设置和时区。与CookieLocaleResolver不同,此策略将本地选择的区域设置存储在Servlet容器的HttpSession中。因此,这些设置对于每个会话都是临时的,因此,当每个会话终止时,这些设置将丢失。
请注意,与外部会话管理机制(如spring session项目)没有直接关系。此SessionLocaleResolver根据当前HttpServletRequest计算并修改相应的HttpSession属性。
Locale Interceptor
你可以通过将LocaleChangeInterceptor添加到其中一个HandlerMapping定义来启用区域设置的更改。它检测请求中的一个参数,并相应地更改区域设置,在dispatcher的应用程序上下文中调用LocaleResolver上的setLocale方法。下一个示例显示,对包含名为siteLanguage的参数的所有*.view资源的调用现在会更改区域设置。例如,请求URL,https://www.sf.net/home.view?siteLanguage=nl,将站点语言更改为荷兰语。以下示例显示如何截获区域设置:
<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>
主题(Themes)
你可以应用spring web mvc框架主题来设置应用程序的整体外观,从而增强用户体验。主题是影响应用程序视觉样式的静态资源的集合,通常是样式表和图像。
定义主题
若要在web应用程序中使用主题,必须设置org.springframework.ui.context.ThemeSource接口。WebApplicationContext接口扩展了资源,但将其职责委托给专用实现。默认情况下,委托是由org.springframework.ui.context.support.resourceBundlethMesource从类路径的根加载属性文件进行实现的。要使用自定义的ThemeSource实现或配置resourceBundlethMesource的基名称前缀,可以在应用程序上下文中使用保留名称ThemeSource注册bean。web应用程序上下文自动检测到具有该名称的bean并使用它。
当你使用resourceBundlethMesource时,在一个简单的属性文件中定义一个主题。属性文件列出了构成主题的资源,如下例所示:
styleSheet=/themes/cool/style.css background=/themes/cool/img/coolBg.jpg
属性的键是从视图代码中引用主题元素的名称。对于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>
解析主题
定义主题后,你将决定使用哪个主题。DispatcherServlet查找名为themeResolver的bean,以确定要使用哪个themeResolver实现。主题解析器的工作方式与LocaleResolver基本相同。它检测要用于特定请求的主题,还可以更改请求的主题。下表介绍了Spring提供的主题解析器:
ThemeResolver 实现:
- FixedThemeResolver:选择一个固定主题,通过使用defaultTheName属性设置。
- SessionThemeResolver:主题在用户的HTTP会话中维护。它只需要为每个会话设置一次,但不会在会话之间持久化。
- CookieThemeResolver:选定的主题存储在客户端的cookie中。
Spring还提供了一个ThemeChangeInterceptor,它可以通过一个简单的请求参数对每个请求进行主题更改。
文件解析(Multipart Resolver)
来自org.springframework.web.multipart包的MultipartResolver是一种分析multipart请求(包括文件上传)的策略。有一种实现基于Commons FileUpload,另一种基于Servlet3.0multipart 请求解析。
要启用multipart 处理,你需要在DispatcherServlet Spring配置中声明一个名为multipartResolver的MultipartResolver bean。DispatcherServlet检测到它并将其应用于传入的请求。当接收到内容类型为multipart/form-data的POST请求时,解析程序解析内容并将当前HttpServletRequest包装为MultipartHttpServletRequest ,以提供对已解析部分的访问,并将其作为请求参数公开。
Apache Commons FileUpload
要使用 Apache Commons FileUpload,你可以配置CommonsMultipartResolver类型的bean,名称为multipartResolver。你还需要引入commons-fileupload依赖。
Servlet 3.0
Servlet3.0 multipart 解析需要通过Servlet容器配置来启用。为此:
- 在Java中,在Servlet注册上设置MultipartConfigElement。
- 在web.xml文件,向servlet声明添加“<multipart config>”部分。
以下示例显示如何在Servlet注册上设置MultipartConfigElement:
public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
// ...
@Override
protected void customizeRegistration(ServletRegistration.Dynamic registration) {
// Optionally also set maxFileSize, maxRequestSize, fileSizeThreshold
registration.setMultipartConfig(new MultipartConfigElement("/tmp"));
}
}
一旦Servlet3.0配置就绪,就可以添加一个名为multipartResolver的StandardServletMultipartResolver类型的bean。
日志
spring mvc中的调试级日志记录被设计成紧凑、最小化和人性化。它关注的是反复有用的高价值信息,而不是只有在调试特定问题时才有用的信息。
跟踪级别日志记录通常遵循与调试相同的原则,但可以用于调试任何问题。此外,一些日志消息在跟踪和调试时可能显示不同级别的详细信息。
良好的日志记录来自使用日志的经验。
敏感数据
调试和跟踪日志记录可能会记录敏感信息。这就是为什么请求参数和头在默认情况下被屏蔽,并且必须通过DispatcherServlet上的enableLoggingRequestDetails属性显式地启用它们。
public class MyInitializer
extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return ... ;
}
@Override
protected Class<?>[] getServletConfigClasses() {
return ... ;
}
@Override
protected String[] getServletMappings() {
return ... ;
}
@Override
protected void customizeRegistration(Dynamic registration) {
registration.setInitParameter("enableLoggingRequestDetails", "true");
}
}