1.1 DispatcherServlet

与其他许多Web框架一样,Spring MVC围绕前端控制器模式进行设计,在该模式下,中央Servlet DispatcherServlet提供了用于请求处理的共享算法,而实际工作是由可配置的委托组件执行的。 该模型非常灵活,并支持多种工作流程。

与任何Servlet一样,都需要根据Servlet规范使用Java配置或在web.xml中声明和映射DispatcherServlet。 反过来,DispatcherServlet使用Spring配置发现请求映射,视图解析,异常处理等所需的委托组件。

以下Java配置示例注册并初始化DispatcherServlet,该容器由Servlet容器自动检测到(请参阅Servlet Config):

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并覆盖特定方法(请参见Context Hierarchy下的示例)。

以下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>

Spring Boot遵循不同的初始化顺序。 Spring Boot并没有陷入Servlet容器的生命周期,而是使用Spring配置来引导自身和嵌入式Servlet容器。 在Spring配置中检测到过滤器和Servlet声明,并在Servlet容器中注册。 有关更多详细信息,请参见Spring Boot文档。

1.1.1 Context 层级关系

DispatcherServlet期望WebApplicationContext(纯ApplicationContext的扩展)为其自身的配置。 WebApplicationContext具有指向ServletContext和与其关联的Servlet的链接。它还绑定到ServletContext,以便应用程序可以在RequestContextUtils上使用静态方法来查找WebApplicationContext(如果需要访问它们)。

对于许多应用程序而言,拥有一个WebApplicationContext很简单并且足够。也可能具有上下文层次结构,其中一个根WebApplicationContext在多个DispatcherServlet(或其他Servlet)实例之间共享,每个实例都有其自己的子WebApplicationContext配置。有关上下文层次结构功能的更多信息,请参见ApplicationContext的其他功能。

根WebApplicationContext通常包含需要在多个Servlet实例之间共享的基础结构Bean,例如数据存储库和业务服务。这些Bean是有效继承的,并且可以在Servlet特定的子WebApplicationContext中重写(即重新声明),该子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参数保留为空。

1.1.2 特殊bean类型

DispatcherServlet委托给特殊的Bean处理请求并呈现适当的响应。 所谓“特殊bean”,是指实现框架协定的Spring管理对象实例。 这些通常带有内置合同,但是您可以自定义它们的属性并扩展或替换它们。

下表列出了DispatcherServlet检测到的特殊bean:

Bean类型说明

HandlerMapping

将请求与拦截器列表一起映射到处理程序,以进行预处理和后期处理。 映射基于某些条件,其细节因HandlerMapping实现而异。HandlerMapping的两个主要实现是RequestMappingHandlerMapping(支持@RequestMapping带注解的方法)和SimpleUrlHandlerMapping(维护对处理程序的URI路径模式的显式注册)。

HandlerAdapter

帮助DispatcherServlet调用映射到请求的处理程序,而不管实际如何调用该处理程序。 例如,调用带注解的控制器需要解析注解。 HandlerAdapter的主要目的是使DispatcherServlet免受此类细节的影响。

HandlerExceptionResolver

解决异常的策略,可能将它们映射到处理程序,HTML错误视图或其他目标。 请参阅例外。

ViewResolver

解析从处理程序返回的实际基于字符串的基于逻辑的视图名称,以实际的视图呈现给响应。 请参阅查看分辨率和查看技术。

LocaleResolver, LocaleContextResolver

解析客户正在使用的语言环境以及可能的时区,以便能够提供国际化的视图。 请参阅区域设置。

ThemeResolver

解决Web应用程序可以使用的主题,例如,以提供个性化的布局。 请参阅主题。

MultipartResolver

借助一些多部分解析库来解析多部分请求的抽象(例如,浏览器表单文件上传)。 请参见多部分分解器。

FlashMapManager

存储和检索“输入”和“输出” FlashMap,可用于将属性从一个请求传递到另一个请求,通常跨重定向。 请参见Flash属性。

1.1.3 Web MVC Config

应用程序可以声明处理请求所需的特殊Bean类型中列出的基础结构Bean。 DispatcherServlet检查每个特殊bean的WebApplicationContext。 如果没有匹配的bean类型,它将使用DispatcherServlet.properties中列出的默认类型。

在大多数情况下,MVC Config是最佳起点。 它使用Java或XML声明所需的bean,并提供了更高级别的配置回调API来对其进行自定义。

1.1.4 Servlet Config

在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是Spring MVC提供的接口,可确保检测到您的实现并将其自动用于初始化任何Servlet 3容器。 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还提供了一种方便的方法来添加Filter实例,并将其自动映射到DispatcherServlet,如以下示例所示:

public class MyWebAppInitializer extends AbstractDispatcherServletInitializer {

    // ...

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

每个过滤器都会根据其具体类型添加一个默认名称,并自动映射到DispatcherServlet。

AbstractDispatcherServletInitializer的isAsyncSupported受保护方法提供了一个位置,以在DispatcherServlet及其映射的所有过滤器上启用异步支持。 默认情况下,此标志设置为true。

最后,如果您需要进一步自定义DispatcherServlet本身,则可以覆盖createDispatcherServlet方法。

1.1.5 Processing

DispatcherServlet处理请求的方式如下:

  • 搜索WebApplicationContext并将其绑定在请求中,作为控制器和流程中其他元素可以使用的属性。默认情况下,它绑定在DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE键下。

  • 语言环境解析器绑定到请求,以使流程中的元素解析在处理请求(呈现视图,准备数据等)时要使用的语言环境。如果不需要语言环境解析,则不需要语言环境解析器。

  • 主题解析器绑定到请求,以使诸如视图之类的元素确定要使用的主题。如果不使用主题,则可以将其忽略。

  • 如果指定多部分文件解析器,则将检查请求中是否有多部分。如果找到多部分,则将该请求包装在MultipartHttpServletRequest中,以供流程中的其他元素进一步处理。有关多部分处理的更多信息,请参见Multipart Resolver。

  • 搜索适当的处理程序。如果找到处理程序,则执行与处理程序(预处理器,后处理器和控制器)关联的执行链,以准备模型或渲染。或者,对于带注解的控制器,可以呈现响应(在HandlerAdapter中),而不是返回视图。

  • 如果返回模型,则呈现视图。如果没有返回任何模型(可能是由于预处理器或后处理器拦截了该请求,可能出于安全原因),则不会呈现任何视图,因为该请求可能已经被满足。

WebApplicationContext中声明的HandlerExceptionResolver Bean用于解决在请求处理期间引发的异常。这些异常解析器允许定制逻辑以解决异常。有关更多详细信息,请参见异常。

Spring DispatcherServlet还支持Servlet API所指定的last-modification-date的返回。确定特定请求的最后修改日期的过程很简单:DispatcherServlet查找适当的处理程序映射,并测试找到的处理程序是否实现了LastModified接口。如果是这样,则将LastModified接口的long getLastModified(request)方法的值返回给客户端。

您可以通过将Servlet初始化参数(init-param元素)添加到web.xml文件中的Servlet声明中,来定制各个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。

1.1.6 Interception

所有HandlerMapping实现都支持处理程序拦截器,当您要将特定功能应用于某些请求时(例如,检查主体),该拦截器很有用。拦截器必须使用三种方法从org.springframework.web.servlet包中实现HandlerInterceptor,这三种方法应提供足够的灵活性来执行各种预处理和后处理:

  • preHandle(..):在执行实际处理程序之前

  • postHandle(..):处理程序执行后

  • afterCompletion(..):在完成请求之后

preHandle(..)方法返回一个布尔值。您可以使用此方法来中断或继续执行链的处理。当此方法返回true时,处理程序执行链将继续。当它返回false时,DispatcherServlet假定拦截器本身已经处理了请求(例如,渲染了一个适当的视图),并且不会继续执行其他拦截器和执行链中的实际处理程序。

有关如何配置拦截器的示例,请参见MVC配置部分中的拦截器。您还可以通过在各个HandlerMapping实现上使用setter直接注册它们。

请注意,对于@ResponseBody和ResponseEntity方法,postHandle的用处不大,在HandlerAdapter内以及postHandle之前,将其写入并提交响应。这意味着对响应进行任何更改为时已晚,例如添加额外的标头。对于这种情况,您可以实现ResponseBodyAdvice并将其声明为Controller Advice Bean或直接在RequestMappingHandlerAdapter上对其进行配置。

1.1.7 Exceptions

如果异常在请求映射期间发生或从请求处理程序(例如@Controller)抛出,则DispatcherServlet委托给HandlerExceptionResolver Bean链来解决该异常并提供替代处理,通常是错误响应。

下表列出了可用的HandlerExceptionResolver实现:

HandlerExceptionResolverDescription

SimpleMappingExceptionResolver

异常类名称和错误视图名称之间的映射。 对于在浏览器应用程序中呈现错误页面很有用。

DefaultHandlerExceptionResolver

解决了Spring MVC引发的异常,并将它们映射到HTTP状态代码。 另请参见备用ResponseEntityExceptionHandler和REST API异常。

ResponseStatusExceptionResolver

使用@ResponseStatus批注解决异常,并根据批注中的值将其映射到HTTP状态代码。

ExceptionHandlerExceptionResolver

通过调用@Controller或@ControllerAdvice类中的@ExceptionHandler方法来解决异常。 请参见@ExceptionHandler方法。

解析器链

您可以通过在Spring配置中声明多个HandlerExceptionResolver bean并根据需要设置其order属性来形成异常解析器链。 order属性越高,异常解析器的定位就越晚。

HandlerExceptionResolver的约定指定它可以返回:

  • 指向错误视图的ModelAndView。

  • 如果在解析程序中处理了异常,则为空的ModelAndView。

  • 如果该异常仍未解决,则为null,以供后续解析器尝试;如果该异常仍在末尾,则允许将其冒泡到Servlet容器。

MVC Config自动为默认的Spring MVC异常,@ResponseStatus注解的异常以及对@ExceptionHandler方法的支持声明内置解析器。您可以自定义该列表或替换它。

容器错误页面

如果任何HandlerExceptionResolver都无法解决异常,因此该异常可以传播,或者如果响应状态设置为错误状态(即4xx,5xx),则Servlet容器可以在HTML中呈现默认错误页面。要自定义容器的默认错误页面,可以在web.xml中声明错误页面映射。以下示例显示了如何执行此操作:

<error-page>
    <location>/error</location>
</error-page>

给定前面的示例,当异常冒出或响应具有错误状态时,Servlet容器在容器内向配置的URL(例如/ error)进行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。

1.1.8 View解析

Spring MVC定义了ViewResolver和View接口,这些接口使您可以在浏览器中呈现模型,而无需将您与特定的视图技术联系在一起。 ViewResolver提供了视图名称和实际视图之间的映射。 在移交给特定的视图技术之前,视图解决了数据准备问题。

下表提供了有关ViewResolver层次结构的更多详细信息:

ViewResolverDescription

AbstractCachingViewResolver

AbstractCachingViewResolver的子类缓存它们解析的视图实例。 缓存可以提高某些视图技术的性能。 您可以通过将cache属性设置为false来关闭缓存。 此外,如果必须在运行时刷新某个视图(例如,当修改FreeMarker模板时),则可以使用removeFromCache(String viewName,Locale loc)方法。

XmlViewResolver

ViewResolver的实现,它接受用XML编写的配置文件,该配置文件的DTD与Spring的XML bean工厂相同。 默认配置文件是/WEB-INF/views.xml。

ResourceBundleViewResolver

在ResourceBundle中使用bean定义的ViewResolver的实现,由包基本名称指定。 对于应该解析的每个视图,它将属性[viewname]。(class)的值用作视图类,并将属性[viewname] .url的值用作视图URL。 您可以在“查看技术”一章中找到示例。

UrlBasedViewResolver

ViewResolver接口的简单实现会影响逻辑视图名称到URL的直接解析,而无需显式映射定义。 如果您的逻辑名称以直接的方式与视图资源的名称匹配,而无需任意映射,则这是适当的。

InternalResourceViewResolver

UrlBasedViewResolver的方便子类,它支持InternalResourceView(实际上是Servlet和JSP)以及诸如JstlView和TilesView之类的子类。 您可以使用setViewClass(..)为该解析器生成的所有视图指定视图类。 有关详细信息,请参见UrlBasedViewResolver Javadoc。

FreeMarkerViewResolver

UrlBasedViewResolver的便利子类,支持FreeMarkerView及其自定义子类。

ContentNegotiatingViewResolver

ViewResolver接口的实现,该接口根据请求文件名或Accept标头解析视图。 请参阅内容协商。

Handling

您可以通过声明多个解析器bean,并在必要时通过设置order属性以指定顺序来链接视图解析器。 请记住,order属性越高,视图解析器在链中的定位就越晚。

ViewResolver的协定指定它可以返回null,以指示找不到该视图。 但是,对于JSP和InternalResourceViewResolver,确定JSP是否存在的唯一方法是通过RequestDispatcher进行调度。 因此,您必须始终将InternalResourceViewResolver配置为在视图解析器的总体顺序中排在最后。

配置视图分辨率就像将ViewResolver bean添加到Spring配置中一样简单。 MVC Config为View解析器和添加无逻辑的View Controller提供了专用的配置API,这对于无需控制器逻辑的HTML模板呈现非常有用。

Redirecting

视图名称中的特殊redirect:前缀使您可以执行重定向。 UrlBasedViewResolver(及其子类)将其识别为需要重定向的指令。 视图名称的其余部分是重定向URL。

最终效果与控制器返回RedirectView的效果相同,但是现在控制器本身可以根据逻辑视图名称进行操作。 逻辑视图名称(例如redirect:/ myapp / some / resource)相对于当前Servlet上下文进行重定向,而名称诸如redirect:https://myhost.com/some/arbitrary/path则重定向到绝对URL。

请注意,如果使用@ResponseStatus注解控制器方法,则注解值优先于RedirectView设置的响应状态。

Forwarding

您还可以使用特殊的forward:前缀表示最终由UrlBasedViewResolver和子类解析的视图名称。 这将创建一个InternalResourceView,它执行RequestDispatcher.forward()。 因此,此前缀在InternalResourceViewResolver和InternalResourceView(对于JSP)中没有用,但是如果您使用另一种视图技术但仍然希望强制转发由Servlet / JSP引擎处理的资源,则该前缀很有用。 请注意,您也可以链接多个视图解析器。

Content Negotiation

ContentNegotiatingViewResolver不会解析视图本身,而是委派给其他视图解析器,并选择类似于客户端请求的表示形式的视图。可以从Accept标头或查询参数(例如,“ / path?format = pdf”)中确定表示形式。

ContentNegotiatingViewResolver通过将请求媒体类型与其与每个ViewResolvers关联的View支持的媒体类型(也称为Content-Type)进行比较,从而选择合适的View处理该请求。列表中具有兼容Content-Type的第一个View将表示形式返回给客户端。如果ViewResolver链无法提供兼容的视图,请查阅通过DefaultViews属性指定的视图列表。后一个选项适用于单身视图,无论逻辑视图名称如何,该视图都可以呈现当前资源的适当表示形式。 Accept标头可以包含通配符(例如text / *),在这种情况下,其Content-Type为text / xml的View是兼容的匹配。

1.1.9 Locale

正如Spring Web MVC框架所做的那样,Spring体系结构的大多数部分都支持国际化。使用DispatcherServlet,您可以使用客户端的语言环境自动解析消息。这是通过LocaleResolver对象完成的。

当请求进入时,DispatcherServlet会寻找一个语言环境解析器,如果找到了它,它会尝试使用它来设置语言环境。通过使用RequestContext.getLocale()方法,您始终可以检索由语言环境解析器解析的语言环境。

除了自动的语言环境解析之外,您还可以在处理程序映射上附加一个拦截器(有关处理程序映射拦截器的更多信息,请参见拦截),以在特定情况下(例如,基于请求中的参数)更改语言环境。

语言环境解析器和拦截器在org.springframework.web.servlet.i18n包中定义,并以常规方式在应用程序上下文中进行配置。 Spring包含以下选择的语言环境解析器。

Time Zone

除了获取客户的语言环境外,了解其时区通常也很有用。 LocaleContextResolver接口提供了LocaleResolver的扩展,该扩展使解析程序可以提供更丰富的LocaleContext,其中可能包含时区信息。

如果可用,可以使用RequestContext.getTimeZone()方法获取用户的TimeZone。通过Spring的ConversionService注册的任何日期/时间转换器和格式器对象都会自动使用时区信息。

Header Resolver

该语言环境解析器检查客户端(例如,Web浏览器)发送的请求中的accept-language标头。通常,此标头字段包含客户端操作系统的语言环境。请注意,此解析器不支持时区信息。

Cookie Resolver

此语言环境解析器检查客户端上可能存在的Cookie,以查看是否指定了语言环境或TimeZone。如果是这样,它将使用指定的详细信息。通过使用此语言环境解析器的属性,可以指定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的属性描述:

PropertyDefaultDescription

cookieName

classname + LOCALE

cookie的名字

cookieMaxAge

Servlet container default

Cookie在客户端上保留的最长时间。 如果指定-1,则cookie将不会保留。 它仅在客户端关闭浏览器之前可用。

cookiePath

/

将Cookie的可见性限制为网站的特定部分。 指定cookiePath时,该cookie仅对该路径及其下方的路径可见。

Session Resolver

通过SessionLocaleResolver,您可以从可能与用户请求关联的会话中检索Locale和TimeZone。与CookieLocaleResolver相比,此策略将本地选择的语言环境设置存储在Servlet容器的HttpSession中。结果,这些设置对于每个会话都是临时的,因此在每个会话终止时会丢失。

请注意,与外部会话管理机制(例如Spring Session项目)没有直接关系。此SessionLocaleResolver针对当前HttpServletRequest评估并修改相应的HttpSession属性。

Locale Interceptor

您可以通过将LocaleChangeInterceptor添加到HandlerMapping定义之一来启用语言环境更改。它检测请求中的参数并相应地更改语言环境,在调度程序的应用程序上下文中在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>

1.1.10 Themes

您可以应用Spring Web MVC框架主题来设置应用程序的整体外观,从而增强用户体验。主题是静态资源的集合,通常影响样式表和图像,这些样式表和图像会影响应用程序的视觉样式。

定义主题

要在Web应用程序中使用主题,您必须设置org.springframework.ui.context.ThemeSource接口的实现。 WebApplicationContext接口扩展了ThemeSource,但将其职责委托给专用的实现。默认情况下,委托是org.springframework.ui.context.support.ResourceBundleThemeSource实现,该实现从类路径的根加载属性文件。要使用自定义ThemeSource实现或配置ResourceBundleThemeSource的基本名称前缀,可以在应用程序上下文中使用保留名称themeSource注册Bean。 Web应用程序上下文会自动检测到具有该名称的bean并使用它。

当您使用ResourceBundleThemeSource时,将在一个简单的属性文件中定义一个主题。属性文件列出了组成主题的资源,如以下示例所示:

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>

默认情况下,ResourceBundleThemeSource使用一个空的基本名称前缀。 结果,从类路径的根加载属性文件。 因此,您可以将cool.properties主题定义放在类路径的根目录中(例如,在/ WEB-INF / classes中)。 ResourceBundleThemeSource使用标准的Java资源束加载机制,允许主题的完全国际化。 例如,我们可以有一个/WEB-INF/classes/cool_nl.properties,它引用带有荷兰文字的特殊背景图像。

Resolving Themes

定义主题后,如上一节所述,您可以决定要使用哪个主题。 DispatcherServlet查找一个名为themeResolver的bean,以找出要使用的ThemeResolver实现。 主题解析器的工作方式与LocaleResolver几乎相同。 它可以检测用于特定请求的主题,还可以更改请求的主题。 下表描述了Spring提供的主题解析器:

ClassDescription

FixedThemeResolver

选择一个固定的主题,该主题通过使用defaultThemeName属性设置。

SessionThemeResolver

该主题在用户的HTTP会话中维护。 每个会话只需设置一次,但在会话之间不会保留。

CookieThemeResolver

所选主题存储在客户端的cookie中。

Spring还提供了ThemeChangeInterceptor,可以使用简单的请求参数对每个请求进行主题更改。

1.1.11 Multipart Resolver

org.springframework.web.multipart包中的MultipartResolver是一种用于解析包括文件上传在内的多部分请求的策略。有一种基于Commons FileUpload的实现,另一种基于Servlet 3.0多部分请求解析。

要启用多部分处理,您需要在DispatcherServlet Spring配置中声明一个名为multipartResolver的MultipartResolver bean。 DispatcherServlet会检测到它并将其应用于传入的请求。当收到内容类型为multipart / form-data的POST时,解析程序将解析内容并将当前的HttpServletRequest包装为MultipartHttpServletRequest,以提供对已解析部分的访问权,此外还可以将其公开为请求参数。

Apache Commons FileUpload

要使用Apache Commons FileUpload,可以配置名称为multipartResolver的CommonsMultipartResolver类型的Bean。您还需要commons-fileupload作为对类路径的依赖。

Servlet 3.0

需要通过Servlet容器配置启用Servlet 3.0多部分解析。为此:

  • 在Java中,在Servlet注册上设置MultipartConfigElement。

  • 在web.xml中,将“ <multipart-config>”部分添加到Servlet声明中。

以下示例显示了如何在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"));
    }

}

Servlet 3.0配置到位后,您可以添加名称为multipartResolver的StandardServletMultipartResolver类型的Bean。

1.1.12 Logging

Spring MVC中的DEBUG级别的日志被设计为紧凑,最少且人性化的。 它侧重于一遍又一遍有用的高价值信息,而其他信息仅在调试特定问题时才有用。

TRACE级别的日志记录通常遵循与DEBUG相同的原则(例如,也不应是消防水带),但可用于调试任何问题。 此外,某些日志消息在TRACE和DEBUG上可能显示不同级别的详细信息。

良好的日志记录来自使用日志的经验。 如果您发现任何不符合既定目标的东西,请告诉我们。

敏感数据

调试和跟踪日志记录可能会记录敏感信息。 这就是默认情况下屏蔽请求参数和标头,并且必须通过DispatcherServlet上的enableLoggingRequestDetails属性显式启用它们的完整登录的原因。

以下示例显示了如何通过使用Java配置来执行此操作:

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");
    }

}

最后更新于