该ModelMap班本质上是一种荣耀Map,可以使补充说,是要显示(或上)一个对象View坚持一个共同的命名约定。考虑以下Controller实现; 注意对象被添加到ModelAndView没有指定的任何关联的名称。
public class DisplayShoppingCartController implements Controller { public ModelAndView handleRequest(HttpServletRequest request,HttpServletResponse response){ 列表cartItems = //获取CartItem对象的列表 User user = //让用户进行购物 ModelAndView mav = new ModelAndView(“displayShoppingCart”); < - 逻辑视图名称 mav.addObject(cartItems); < - 看ma,没有名字,只是对象 mav.addObject(用户); - 再次! 返回 mav } }的ModelAndView类使用一个ModelMap类,它是一个自定义的Map,可自动生成用于当对象被添加到它的对象的键实现。在标量对象的情况下,用于确定添加对象的名称的策略是User使用对象类的短类名。以下示例是为放置到ModelMap实例中的标量对象生成的名称。
x.y.User添加 的实例将生成名称user。 x.y.Registration添加 的实例将生成名称registration。 x.y.Foo添加 的实例将生成名称foo。 java.util.HashMap添加 的实例将生成名称hashMap。在这种情况下,您可能想要明确说明名称,因为hashMap它不直观。添加null将导致IllegalArgumentException被抛出。如果您要添加的对象(或对象)可能是null,那么您还需要明确说明名称。什么,没有自动多元化?
Spring Web MVC的常规配置支持不支持自动多元化。也就是说,你不能添加List的Person对象的ModelAndView ,并有生成的名字会people。
这个决定是经过一番辩论,最终赢得了“最不起眼的原则”。
在添加一个Set或一个之后生成名称的策略List是窥视集合,获取集合中第一个对象的短类名称,并将其List附加到名称后面。这同样适用于数组,尽管数组不需要窥视数组内容。几个例子将使得集合名称生成的语义更加清晰:
添加x.y.User[]零个或多个x.y.User元素 的数组将生成名称 userList。添加x.y.Foo[]零个或多个x.y.User元素 的数组将生成名称 fooList。添加java.util.ArrayList一个或多个x.y.User元素的 A 将生成名称 userList。添加java.util.HashSet一个或多个x.y.Foo元素的 A 将生成名称 fooList。根本不会添加 一个空 java.util.ArrayList(实际上, addObject(..)调用本质上是无操作的)。该RequestToViewNameTranslator接口确定的逻辑View当没有这样的逻辑视图名称被明确地提供的名称。它只有一个实现,DefaultRequestToViewNameTranslator类。
该DefaultRequestToViewNameTranslator请求的URL映射到逻辑视图的名称,与该实施例中:
public class RegistrationController implements Controller { public ModelAndView handleRequest(HttpServletRequest request,HttpServletResponse response){ //处理请求... ModelAndView mav = new ModelAndView(); //根据需要添加数据到模型... return mav; //注意没有设置View或逻辑视图名称 } } <?xml version =“1.0”encoding =“UTF-8”?> <beans xmlns = “http://www.springframework.org/schema/beans” xmlns:xsi = “http://www.w3.org / 2001 / XMLSchema-instance“ xsi:schemaLocation = ” http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd“ > <! - 这个具有众所周知的名称的bean为我们生成视图名称 - > <bean id = “viewNameTranslator” class = “org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator” /> <bean class = “xyRegistrationController” > <! - 根据需要注入依赖项 - > </ bean> <! - 将请求URL映射到控制器名称 - > <bean class = “org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping” /> <bean id = “viewResolver” class = “org.springframework.web.servlet.view.InternalResourceViewResolver” > <property name = “prefix” value = “/ WEB-INF / jsp /” /> <property name = “suffix” value = “.jsp” /> </ bean> </豆>注意在执行handleRequest(..)方法no View或逻辑视图名称是否被设置ModelAndView返回的。该 DefaultRequestToViewNameTranslator任务是 从请求的URL 生成逻辑视图名称。另外,在上述的情况下RegistrationController,这是在与结合使用ControllerClassNameHandlerMapping的一个请求的URLhttp://localhost/registration.html中的一个逻辑视图名称结果registration 被生成的DefaultRequestToViewNameTranslator。该逻辑视图名称然后/WEB-INF/jsp/registration.jsp由InternalResourceViewResolverbean 解析为视图 。
您不需要明确定义一个DefaultRequestToViewNameTranslatorbean。如果您喜欢默认设置DefaultRequestToViewNameTranslator,可以依靠Spring Web MVC DispatcherServlet实例化此类的实例,如果未明确配置。当然,如果您需要更改默认设置,那么您需要明确配置自己的DefaultRequestToViewNameTranslatorbean。有关可配置DefaultRequestToViewNameTranslator的各种属性的详细信息,请查阅全面的 javadocs。
良好的HTTP缓存策略可以显着提高Web应用程序的性能和客户体验。所述'Cache-Control'HTTP响应报头主要是为这个负责,使用条件报头,例如沿'Last-Modified'和'ETag'。
该'Cache-Control'HTTP响应头劝告私有的高速缓存(如浏览器),以及他们如何缓存进一步重用HTTP响应的公共高速缓存(例如代理)。
一个的ETag(实体标签)是由HTTP / 1.1兼容的Web用于在给定的URL来确定内容改变服务器返回的HTTP响应报头中。它可以被认为是Last-Modified标题的更复杂的继承者 。当服务器返回具有ETag头的表示时,客户端可以在标题中的后续GET中使用此If-None-Match头。如果内容没有改变,服务器返回304: Not Modified。
本节介绍在Spring Web MVC应用程序中配置HTTP缓存的不同选择。
Spring Web MVC支持许多用例和方式为应用程序配置“Cache-Control”标头。虽然RFC 7234第5.2.2节 完全描述了标题及其可能的指令,但是有几种方法可以解决最常见的情况。
Spring Web MVC在其几个API中使用配置约定 setCachePeriod(int seconds):
甲-1值将不生成'Cache-Control'响应头。一个0值,将使用防止缓存'Cache-Control: no-store'指令。一个n > 0值将缓存对于给定的响应n使用秒 'Cache-Control: max-age=n'指令。该CacheControl生成器类简单地描述了可用的“缓存控制”指令,并使其更容易建立自己的HTTP缓存策略。一旦构建,一个CacheControl实例就可以被接受为几个Spring Web MVC API中的参数。
//缓存一小时 - “Cache-Control:max-age = 3600” CacheControl ccCacheOneHour = CacheControl.maxAge(1,TimeUnit.HOURS); //防止缓存 - “Cache-Control:no-store” CacheControl ccNoStore = CacheControl.noStore(); //在公共和私有缓存中缓存十天, //公共缓存不应该转换响应 //“Cache-Control:max-age = 864000,public,no-transform” CacheControl ccCustom = CacheControl.maxAge(10,TimeUnit 。天) 。.noTransform()cachePublic();应使用适当的'Cache-Control'条件标题来提供静态资源,以获得最佳性能。 配置一个ResourceHttpRequestHandler用于提供静态资源的功能不仅'Last-Modified'通过读取文件的元数据来定位'Cache-Control'头文件,而且还可以正确配置头文件。
您可以设置cachePeriod属性ResourceHttpRequestHandler或使用CacheControl实例,该实例支持更具体的指令:
@Configuration @EnableWebMvc public class WebConfig extends WebMvcConfigurerAdapter { @Override public void addResourceHandlers(ResourceHandlerRegistry registry){ registry.addResourceHandler(“/ resources / **”) .addResourceLocations(“/ public-resources /”) .setCacheControl(CacheControl.maxAge(1,TimeUnit.HOURS).cachePublic()); } }在XML中:
<mvc:resources mapping = “/ resources / **” location = “/ public-resources /” > <mvc:cache-control max-age = “3600” cache-public = “true” /> </ mvc:resources >控制器可以支持'Cache-Control','ETag'和/或'If-Modified-Since'HTTP请求; 如果'Cache-Control'要在响应中设置标题,则建议这样做。这涉及计算long给定请求的lastModified 和/或Etag值,将其与'If-Modified-Since'请求头值进行比较,并可能返回具有状态代码304(未修改)的响应。
如“使用HttpEntity”一节所述,控制器可以使用HttpEntity类型与请求/响应进行交互 。返回的控制器ResponseEntity可以包括响应中的HTTP缓存信息,如下所示:
@GetMapping(“/ book / {id}”) public ResponseEntity <Book> showBook( @PathVariable Long id){ Book book = findBook(id); String version = book.getVersion(); 返回 ResponseEntity 。好() .cacheControl(CacheControl.maxAge(30,TimeUnit.DAYS)) .eTag(version)// lastModified也可用 。体(书); }如果客户端发送的条件标头与控制器设置的缓存信息匹配,这样做将不仅包括响应中的头'ETag'和'Cache-Control'头,还会将响应转换HTTP 304 Not Modified为空体。
一种@RequestMapping方法也不妨支持相同的行为。这可以实现如下:
@RequestMapping public String myHandleMethod(WebRequest webRequest,Model model){ long lastModified = // 1.应用程序特定的计算 if(request.checkNotModified(lastModified)){ // 2.快捷方式退出 - 无需进一步处理必需 返回 null; } // 3.否则进一步请求处理,实际准备内容 model.addAttribute(...); 返回 “myViewName” ; }这里有两个关键元素:呼叫request.checkNotModified(lastModified)和返回null。前者在返回之前设置适当的响应状态和标题true。后者与前者相结合,导致Spring MVC不再进一步处理请求。
请注意,有3种变体:
request.checkNotModified(lastModified)与比较上次更改时间 'If-Modified-Since'或'If-Unmodified-Since'请求头 request.checkNotModified(eTag)将eTag与'If-None-Match'请求标头 进行比较 request.checkNotModified(eTag, lastModified) 这两者都意味着两个条件都应该是有效的当接收到条件'GET'/ 'HEAD'请求时,checkNotModified将检查资源是否未被修改,如果是这样,它将导致HTTP 304 Not Modified 响应。在有条件的'POST'/ 'PUT'/ 'DELETE'请求的情况下,checkNotModified 将检查资源是否尚未被修改,如果已经被修改,它将导致 HTTP 409 Precondition Failed响应以防止并发修改。
支持ETag由Servlet过滤器提供ShallowEtagHeaderFilter。它是一个简单的Servlet过滤器,因此可以与任何Web框架结合使用。该 ShallowEtagHeaderFilter滤波器产生所谓的浅ETag的(相对于深的ETag,稍后详细说明)。该过滤器缓存呈现JSP(或其他内容)的内容,产生超过其MD5哈希,并返回作为ETag头在回应中。下一次客户端发送对同一资源的请求时,它将使用该哈希作为该If-None-Match值。过滤器检测到这一点,再次渲染视图,并比较两个散列。如果它们相等,304则返回a。
请注意,此策略可节省网络带宽但不节省CPU,因为必须为每个请求计算完整的响应。控制器级别的其他策略(如上所述)可以节省网络带宽并避免计算。
此过滤器具有一个writeWeakETag参数,可将过滤器配置为写入弱符号ETags,如RFC 7232第2.3节W/"02a2d595e6ed9a0b24f027f2b63b134d6"中所定义 。
您配置ShallowEtagHeaderFilter的web.xml:
<filter> <filter-name> etagFilter </ filter-name> <filter-class> org.springframework.web.filter.ShallowEtagHeaderFilter </ filter-class> <! - 配置过滤器写入弱ETag的可选参数 <INIT-PARAM> <PARAM名称> writeWeakETag </ PARAM名称> <PARAM值>真</ PARAM值> </ INIT-param> - > </ filter> <filter-mapping> <filter-name> etagFilter </ filter-name> <servlet-name> petclinic </ servlet-name> </ filter-mapping>或者在Servlet 3.0+环境中,
public class MyWebAppInitializer extends AbstractDispatcherServletInitializer { // ... @Override protected Filter [] getServletFilters(){ return new Filter [] { new ShallowEtagHeaderFilter()}; } }有关详细信息,请参见第18.15节“基于代码的Servlet容器初始化”。
在Servlet 3.0+环境中,您可以选择以编程方式配置Servlet容器作为替代方法或与web.xml文件组合使用。下面是一个注册一个例子DispatcherServlet:
import org.springframework.web.WebApplicationInitializer; public class MyWebApplicationInitializer 实现 WebApplicationInitializer { @Override public void onStartup(ServletContext container){ XmlWebApplicationContext appContext = new XmlWebApplicationContext(); appContext.setConfigLocation(“/WEB-INF/spring/dispatcher-config.xml”); ServletRegistration.Dynamic注册= container.addServlet(“dispatcher”,新的 DispatcherServlet(appContext)); registration.setLoadOnStartup(1); registration.addMapping(“/”); } }WebApplicationInitializer是由Spring MVC提供的接口,可确保您的实现被检测并自动用于初始化任何Servlet 3容器。WebApplicationInitializer命名 的抽象基类实现通过简单地覆盖方法来指定servlet映射和配置的位置AbstractDispatcherServletInitializer更容易注册 。DispatcherServletDispatcherServlet
推荐使用基于Java的Spring配置的应用程序:
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class <?> [] getRootConfigClasses(){ return null; } @Override protected Class <?> [] getServletConfigClasses(){ return new Class [] {MyWebConfig。类 }; } @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”); 返回 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。
提供单一位置的isAsyncSupported受保护方法AbstractDispatcherServletInitializer可以在DispatcherServlet映射到它的所有过滤器上启用异步支持。默认情况下,该标志设置为true。
最后,如果需要进一步自定义DispatcherServlet自身,可以覆盖该createDispatcherServlet方法。