zl程序教程

您现在的位置是:首页 >  其他

当前栏目

《Spring 5 官方文档》18. Web MVC 框架(十一)

2023-03-14 10:32:16 时间

18.16.6 Content Negotiation

You can configure how Spring MVC determines the requested media types from the request. The available options are to check the URL path for a file extension, check the “Accept” header, a specific query parameter, or to fall back on a default content type when nothing is requested. By default the path extension in the request URI is checked first and the “Accept” header is checked second.

The MVC Java config and the MVC namespace register jsonxmlrssatom by default if corresponding dependencies are on the classpath. Additional path extension-to-media type mappings may also be registered explicitly and that also has the effect of whitelisting them as safe extensions for the purpose of RFD attack detection (see the section called “Suffix Pattern Matching and RFD” for more detail).

Below is an example of customizing content negotiation options through the MVC Java config:

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

	@Override
	public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
		configurer.mediaType("json", MediaType.APPLICATION_JSON);
	}
}

In the MVC namespace, the <mvc:annotation-driven> element has a content-negotiation-manager attribute, which expects a ContentNegotiationManagerthat in turn can be created with a ContentNegotiationManagerFactoryBean:

<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager"/>

<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
	<property name="mediaTypes">
		<value>
			json=application/json
			xml=application/xml
		</value>
	</property>
</bean>

If not using the MVC Java config or the MVC namespace, you’ll need to create an instance of ContentNegotiationManager and use it to configure RequestMappingHandlerMapping for request mapping purposes, and RequestMappingHandlerAdapter and ExceptionHandlerExceptionResolver for content negotiation purposes.

Note that ContentNegotiatingViewResolver now can also be configured with a ContentNegotiationManager, so you can use one shared instance throughout Spring MVC.

In more advanced cases, it may be useful to configure multiple ContentNegotiationManager instances that in turn may contain customContentNegotiationStrategy implementations. For example you could configure ExceptionHandlerExceptionResolver with a ContentNegotiationManagerthat always resolves the requested media type to "application/json". Or you may want to plug a custom strategy that has some logic to select a default content type (e.g. either XML or JSON) if no content types were requested.

18.16.7 View Controllers

This is a shortcut for defining a ParameterizableViewController that immediately forwards to a view when invoked. Use it in static cases when there is no Java controller logic to execute before the view generates the response.

An example of forwarding a request for "/" to a view called "home" in Java:

@Configuration 
@EnableWebMvc
 public  class WebConfig extends WebMvcConfigurerAdapter {

	@Override
	 public  void addViewController(ViewControllerRegistry registry){
		registry.addViewController(“/”).setViewName(“home”);
	}

}

而且在XML中使用相同的<mvc:view-controller>元素:

<mvc:view-controller  path = “/”  view-name = “home” />

18.16.8查看解决方案

MVC配置简化了视图解析器的注册。

以下是一个Java配置示例,它使用FreeMarker HTML模板配置内容协商视图分辨率,并将Jackson作为ViewJSON渲染的默认值:

@Configuration 
@EnableWebMvc
 public  class WebConfig extends WebMvcConfigurerAdapter {

	@Override
	 public  void configureViewResolvers(ViewResolverRegistry registry){
		registry.enableContentNegotiation(new MappingJackson2JsonView());
		registry.jsp();
	}

}

和XML一样:

<mvc:view-resolvers> 
	<mvc:content-negotiation> 
		<mvc:default-views> 
			<bean  class = “org.springframework.web.servlet.view.json.MappingJackson2JsonView” /> 
		</ mvc:default-views> 
	</ mvc:content-negotiation> 
	<mvc:jsp /> 
</ mvc:view-resolvers>

不过请注意,FreeMarker,Tiles,Groovy Markup和脚本模板也需要配置底层视图技术。

MVC命名空间提供专用元素。例如与FreeMarker:

<mvc:view-resolvers> 
	<mvc:content-negotiation> 
		<mvc:default-views> 
			<bean  class = “org.springframework.web.servlet.view.json.MappingJackson2JsonView” /> 
		</ mvc:default-views> 
	</ mvc:content-negotiation> 
	<mvc:freemarker  cache = “false” /> 
</ mvc:view-resolvers>

<mvc:freemarker-configurer> 
	<mvc:template-loader-path  location = “/ freemarker” /> 
</ mvc:freemarker-configurer>

在Java配置中,只需添加相应的“Configurer”bean:

@Configuration 
@EnableWebMvc
 public  class WebConfig extends WebMvcConfigurerAdapter {

	@Override
	 public  void configureViewResolvers(ViewResolverRegistry registry){
		registry.enableContentNegotiation(new MappingJackson2JsonView());
		。registry.freeMarker()高速缓存(假);
	}

	@Bean
	 public FreeMarkerConfigurer freeMarkerConfigurer(){
		FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
		configurer.setTemplateLoaderPath(“/ WEB-INF /”);
		return configurer;
	}

}

18.16.9资源服务

此选项允许在特定URL模式之后的静态资源请求由位置ResourceHttpRequestHandler列表中的任何一个Resource提供。这提供了一种方便的方式来从除Web应用程序根以外的位置(包括类路径上的位置)提供静态资源。该cache-period属性可用于设置远期未来的到期标头(1年是优化工具的推荐,如Page Speed和YSlow),以便客户端更有效地利用它们。处理程序也正确地评估Last-Modified标题(如果存在),以便适当地304返回状态代码,避免客户端已经缓存的资源的不必要的开销。例如,/resources/**public-resources

@Configuration 
@EnableWebMvc
 public  class WebConfig extends WebMvcConfigurerAdapter {

	@Override
	 public  void addResourceHandlers(ResourceHandlerRegistry registry){
		registry.addResourceHandler(“/ resources / **”).addResourceLocations(“/ public-resources /”);
	}

}

和XML一样:

<mvc:resources  mapping = “/ resources / **”  location = “/ public-resources /” />

为了在未来1年的时间内为这些资源提供服务,以确保最大限度地利用浏览器缓存并减少浏览器发出的HTTP请求:

@Configuration 
@EnableWebMvc
 public  class WebConfig extends WebMvcConfigurerAdapter {

	@Override
	 public  void addResourceHandlers(ResourceHandlerRegistry registry){
		registry.addResourceHandler(“/资源/ **”).addResourceLocations(“/公共资源/”).setCachePeriod(31556926);
	}

}

在XML中:

<mvc:resources  mapping = “/ resources / **”  location = “/ public-resources /”  cache-period = “31556926” />

有关详细信息,请参阅静态资源的HTTP缓存支持

mapping属性必须是可以被使用的蚂蚁图案 SimpleUrlHandlerMapping,并且location属性必须指定一个或多个有效的资源目录位置。可以使用逗号分隔的值列表来指定多个资源位置。指定的位置将按照指定的顺序检查是否存在任何给定请求的资源。例如,要启用来自Web应用程序根目录和类路径/META-INF/public-web-resources/中任何jar中已知路径 的资源的使用,请执行以下操作:

@EnableWebMvc 
@Configuration
 public  class WebConfig extends WebMvcConfigurerAdapter {

	@Override
	 public  void addResourceHandlers(ResourceHandlerRegistry registry){
		registry.addResourceHandler(“/ resources / **”)
				.addResourceLocations(“/”“classpath:/ META-INF / public-web-resources /”);
	}

}

在XML中:

<mvc:resources  mapping = “/ resources / **”  location = “/,classpath:/ META-INF / public-web-resources /” />

当部署新版本的应用程序时可能会更改的资源时,建议您将版本字符串合并到用于请求资源的映射模式中,以便您可以强制客户端请求新部署的应用程序资源版本。版本化URL的支持内置在框架中,可以通过在资源处理程序上配置资源链来启用。该链由ResourceResolver 一个或多个ResourceTransformer实例之后的一个实例组成。他们一起可以提供任意解决和资源转型。

内置VersionResourceResolver可配置不同的策略。例如,FixedVersionStrategy可以使用属性,日期或其他作为版本。A ContentVersionStrategy使用从资源的内容计算的MD5哈希(称为“指纹识别”URL)。请注意,VersionResourceResolver在服务资源时,会自动将解析的版本字符串用作HTTP ETag标头值。

ContentVersionStrategy是一个很好的默认选择,除非不能使用(例如使用JavaScript模块加载器)的情况。您可以针对不同的模式配置不同的版本策略,如下所示。请记住,计算基于内容的版本是昂贵的,因此在生产中应该启用资源链缓存。

Java配置示例

@Configuration 
@EnableWebMvc
 public  class WebConfig extends WebMvcConfigurerAdapter {

	@Override
	 public  void addResourceHandlers(ResourceHandlerRegistry registry){
		registry.addResourceHandler(“/ resources / **”)
				.addResourceLocations(“/ public-resources /”)
				.resourceChain(真).addResolver(
					new VersionResourceResolver()。addContentVersionStrategy(“/ **”));
	}

}

XML示例:

<MVC:资源 映射 = “/资源/ **”  位置 = “/公共资源/” > 
	<MVC:资源链> 
		<MVC:资源高速缓存/> 
		<MVC:解析器> 
			<MVC:版本-解析器> 
				<mvc:content-version-strategy  patterns = “/ **” /> 
			</ mvc:version-resolver> 
		</ mvc:resolvers> 
	</ mvc:resource-chain> 
</ mvc:resources>

为了使上述工作,应用程序还必须使用版本来呈现URL。最简单的方法是配置 ResourceUrlEncodingFilter其中包含响应并覆盖其encodeURL方法。这将在JSP,FreeMarker以及调用响应encodeURL方法的任何其他视图技术中起作用 。或者,应用程序还可以直接注入和使用 ResourceUrlProviderbean,该bean将自动使用MVC Java配置和MVC命名空间声明。

还支持Webjars WebJarsResourceResolver,当"org.webjars:webjars-locator"库位于类路径时,Webjars会自动注册。该解析器允许资源链从HTTP GET请求中解析版本不可知库 "GET /jquery/jquery.min.js"将返回资源"/jquery/1.2.0/jquery.min.js"。它也可以通过在模板中重写资源URL来实现<script src="/jquery/jquery.min.js"/> → <script src="/jquery/1.2.0/jquery.min.js"/>

18.16.10回退在“默认”Servlet服务资源

这允许映射DispatcherServlet到“/”(从而覆盖容器的默认Servlet的映射),同时仍允许静态资源请求由容器的默认Servlet处理。它DefaultServletHttpRequestHandler使用“/ **”的URL映射和相对于其他URL映射的最低优先级来配置。

该处理程序将所有请求转发到默认Servlet。因此,重要的是按照所有其他URL的顺序保持最后HandlerMappings。如果您使用<mvc:annotation-driven>或者如果您正在设置自己的自定义HandlerMapping实例,请确保将其order属性设置为低于该值的值DefaultServletHttpRequestHandler,否则将是这种情况Integer.MAX_VALUE

要使用默认设置启用该功能,请使用:

@Configuration 
@EnableWebMvc
 public  class WebConfig extends WebMvcConfigurerAdapter {

	@Override
	 public  void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer){
		configurer.enable();
	}

}

或在XML中:

<MVC:默认servlet的处理程序/>

覆盖“/”Servlet映射的注意事项是,RequestDispatcher默认的Servlet必须通过名称而不是路径检索。该 DefaultServletHttpRequestHandler会尝试自动检测在启动时容器中的默认的Servlet,使用大多数主要的Servlet容器(包括软件Tomcat,Jetty的GlassFish,JBoss和树脂中,WebLogic和WebSphere)已知名称的列表。如果默认的Servlet已经使用不同的名称自定义配置,或者如果使用了默认Servlet名称未知的其他Servlet容器,那么必须明确地提供默认的Servlet名称,如下例所示:

@Configuration 
@EnableWebMvc
 public  class WebConfig extends WebMvcConfigurerAdapter {

	@Override
	 public  void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer){
		configurer.enable(“myCustomDefaultServlet”);
	}

}

或在XML中:

<mvc:default-servlet-handler  default-servlet-name = “myCustomDefaultServlet” />

18.16.11路径匹配

这允许自定义与URL映射和路径匹配相关的各种设置。有关各个选项的详细信息,请查看 PathMatchConfigurer API。

以下是Java配置中的一个示例:

@Configuration 
@EnableWebMvc
 public  class WebConfig extends WebMvcConfigurerAdapter {

	@Override
	 public  void configurePathMatch(PathMatchConfigurer configurer){
		配置者
		    .setUseSuffixPatternMatch(真)
		    .setUseTrailingSlashMatch(假)
		    .setUseRegisteredSuffixPatternMatch(真)
		    .setPathMatcher(antPathMatcher())
		    .setUrlPathHelper(urlPathHelper());
	}

	@Bean
	 public UrlPathHelper urlPathHelper(){
	     // ...
	}

	@Bean
	 public PathMatcher antPathMatcher(){
	     // ...
	}

}

和XML一样,使用<mvc:path-matching>元素:

<mvc:annotation-driven>
    <mvc:path-matching
        suffix-pattern="true"
        trailing-slash="false"
        registered-suffixes-only="true"
        path-helper="pathHelper"
        path-matcher="pathMatcher"/>
</mvc:annotation-driven>

<bean id="pathHelper" class="org.example.app.MyPathHelper"/>
<bean id="pathMatcher" class="org.example.app.MyPathMatcher"/>

18.16.12消息转换器

如果要替换由Spring MVC创建的默认转换器,或者 如果您只想自定义它们或将额外的转换器添加到默认转换器,HttpMessageConverter则可以通过重写Java配置来实现 定制。configureMessageConverters()extendMessageConverters()

以下是使用自定义ObjectMapper而不是默认值添加Jackson JSON和XML转换器的示例 :

@Configuration
@EnableWebMvc
public class WebConfiguration extends WebMvcConfigurerAdapter {

	@Override
	public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
		Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder()
				.indentOutput(true)
				.dateFormat(new SimpleDateFormat("yyyy-MM-dd"))
				.modulesToInstall(new ParameterNamesModule());
		converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
		converters.add(new MappingJackson2XmlHttpMessageConverter(builder.xml().build()));
	}在此示例中,Jackson2ObjectMapperBuilder用于为两者创建通用配置,MappingJackson2HttpMessageConverterMappingJackson2XmlHttpMessageConverter启用缩进,自定义日期格式以及 jackson-module-parameter-name的注册 ,以增加对访问参数名称的支持(Java 8中添加的功能)。ngJackson2XmlHttpMessageConverter with indentation enabled, a customized date format and the registration of jackson-module-parameter-names that adds support for accessing parameter names (feature added in Java 8).
[注意]
使用Jackson XML支持启用缩进功能需要 woodstox-core-asl 除依赖之外jackson-dataformat-xml

其他有趣的杰克逊模块可用:

  1. jackson-datatype-money:支持javax.money类型(非官方模块)
  2. jackson-datatype-hibernate:支持Hibernate的特定类型和属性(包括延迟加载方面)

也可以在XML中执行相同的操作:

<mvc:annotation-driven> 
    <mvc:message-converters> 
        <bean  class = “org.springframework.http.converter.json.MappingJackson2HttpMessageConverter” > 
            <property  name = “objectMapper”  ref = “objectMapper” /> 
        </ bean> 
        <bean  class = “org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter” > 
            <property  name = “objectMapper”  ref = “xmlMapper” /> 
        </ bean> 
    </ mvc:message-converters> 
</ mvc:注解驱动>

<bean  id = “objectMapper”  class = “org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean” 
      p:indentOutput = “true” 
      p:simpleDateFormat = “yyyy-MM-dd” 
      p:modulesToInstall = “com.fasterxml.jackson .module.paramnames.ParameterNamesModule“ />

<bean  id = “xmlMapper”  parent = “objectMapper”  p:createXmlMapper = “true” />

18.16.13使用MVC Java Config进行高级定制

从上述示例可以看出,MVC Java配置和MVC命名空间提供了不需要深入了解为您创建的基础bean的更高级别的构造。相反,它可以帮助您专注于您的应用程序需求。但是,在某些时候,您可能需要更细致的控制,或者您可能只想了解底层配置。

更精细控制的第一步是查看为您创建的基础bean。在MVC Java配置中,您可以看到javadoc和其中的@Bean方法 WebMvcConfigurationSupport。此类中的配置通过注释自动导入@EnableWebMvc。其实如果你打开@EnableWebMvc你可以看到@Import声明。

更精细控制的下一步是定制一个在其中创建的bean之一WebMvcConfigurationSupport或可能提供您自己的实例的属性。这需要两件事情 – 删除@EnableWebMvc注释,以防止导入,然后从DelegatingWebMvcConfiguration一个子类 扩展WebMvcConfigurationSupport。这是一个例子:

@Configuration
 public  class WebConfig extends DelegatingWebMvcConfiguration {

	@Override
	 public  void addInterceptors(InterceptorRegistry registry){
		 // ...
	}

	@Override 
	@Bean
	 public RequestMappingHandlerAdapter requestMappingHandlerAdapter(){
		 //创建或让“超级”创建适配器
		//然后自定义其属性之一
	}

}
[注意]
应用程序应该只有一个配置扩展DelegatingWebMvcConfiguration 或单个@EnableWebMvc注释类,因为它们都注册了相同的底层bean。以这种方式修改bean不会阻止您使用本节前面显示的任何更高级别的构造。WebMvcConfigurerAdapter子类和 WebMvcConfigurer实现仍在使用中。

18.16.14使用MVC命名空间进行高级自定义

对您创建的配置的细粒度控制对于MVC命名空间来说有点困难。

如果您确实需要这样做,而不是复制其提供的配置,请考虑配置一个BeanPostProcessor检测要按类型自定义的bean,然后根据需要修改其属性。例如:

@Component
 public  class MyPostProcessor实现 BeanPostProcessor {

	public Object postProcessBeforeInitialization(Object bean,String name)throws BeansException {
		 if(Bean instanceof RequestMappingHandlerAdapter){
			 //修改适配器的属性
		}
	}

}

请注意,MyPostProcessor需要将其包含在其中<component scan/>以便检测到它,或者如果您喜欢,可以使用XML bean声明明确声明它。