一、HTTP缓存java
HTTP缓存能够显著提升web应用程序的性能。HTTP缓存围绕着Cache-Control响应头和后续的条件请求头(如Last-Modified
和ETag)进行。Cache-Control建议私有(例如,浏览器)和公共(例如,代理)缓存如何缓存和重用响应。ETag头用于发出条件请求,若是内容没有更改,则可能致使304(NOT_MODIFIED)没有正文。ETag能够看做是Last-Modified文件的更复杂的继承者。jquery
本节讲 Spring WebFlux中可用的与HTTP缓存相关的方式。web
1.一、CacheControlspring
CacheControl
支持配置与Cache-Control标头相关的设置,并在许多位置被接受为参数:sql
Controllers编程
静态Resourcesjson
虽然RFC 7234描述了缓存Cache-Control头的全部可能的指令,但CacheControl
类型采用了面向用例的方法,重点关注常见场景,以下例所示:api
//缓存一小时-“Cache-Control: max-age=3600”
CacheControl ccCacheOneHour = CacheControl.maxAge(1, TimeUnit.HOURS);
// 防止缓存 - "Cache-Control: no-store"
CacheControl ccNoStore = CacheControl.noStore();
// 在公共缓存和私有缓存中缓存10天,公共缓存不该转换响应
// "Cache-Control: max-age=864000, public, no-transform"
CacheControl ccCustom = CacheControl.maxAge(10, TimeUnit.DAYS).noTransform().cachePublic();
1.二、Controllers浏览器
控制器能够添加对HTTP缓存的支持。建议这样作,由于在将资源与条件请求头进行比较以前,须要计算资源的lastModified
或ETag
值。控制器能够向ResponseEntity添加ETag
和Cache-Control设置,以下例所示:缓存
public ResponseEntity<Book> showBook( Long id) {
Book book = findBook(id);
String version = book.getVersion();
return ResponseEntity
.ok()
.cacheControl(CacheControl.maxAge(30, TimeUnit.DAYS))
.eTag(version) // lastModified也可用
.body(book);
}
若是与条件请求头的比较代表内容没有更改,则前面的示例发送一个304(NOT_MODIFIED)响应,该响应的主体为空。不然,ETag
和Cache-Control头将被添加到响应中。
还能够在控制器中对条件请求头进行检查,以下例所示:
public String myHandleMethod(ServerWebExchange exchange, Model model) {
long eTag = ... //应用程序指定计算。
if (exchange.checkNotModified(eTag)) {
return null; //响应已设置为304(未修改)。无需进一步处理。
}
model.addAttribute(...); //继续请求处理
return "myViewName";
}
根据eTag
值、lastModified
值或二者检查条件请求有三种变体。对于有条件的GET
和HEAD
请求,能够将响应设置为304(NOT_MODIFIED)。对于条件POST、PUT和DELETE,能够将响应设置为409(PRECONDITION_FAILED),以防止并发修改。
1.三、静态Resources
你应该为静态资源提供缓存控件和条件响应头,以得到最佳性能。
public class WebConfig implements WebFluxConfigurer {
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**")
.addResourceLocations("/public", "classpath:/static/")
.setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS));
}
}
二、WebFlux 配置
WebFlux ava配置声明了使用带注解的控制器或functional endpoints处理请求所需的组件,并提供了一个API来定制配置。这意味着你不须要理解由Java配置建立的底层bean。可是,若是想了解它们,能够在WebFluxConfigurationSupport
中看到它们,或者阅读有关它们在特殊Bean类型中的更多信息。
对于配置API中不可用的更高级自定义,能够经过高级配置模式得到对配置的彻底控制。
2.一、启用WebFlux配置
能够在Java配置中使用@EnableWebFlux注解,以下例所示:
public class WebConfig {
}
前面的示例注册了许多Spring WebFlux bean,并对JSON、XML等classpath - 上可用的依赖关系进行了调整。
2.二、WebFlux配置API
在Java配置中,能够实现WebFluxConfigurer
接口,以下例所示:
public class WebConfig implements WebFluxConfigurer {
// 实现配置方法。。。
}
2.三、转换,格式化
默认状况下,安装了Number
和Date
类型的格式化程序,包括对@NumberFormat和@DateTimeFormat注解的支持。若是classpath中存在Joda时间,则还安装了对Joda时间格式库的彻底支持。
如下示例显示如何注册自定义格式设置程序和转换器:
public class WebConfig implements WebFluxConfigurer {
public void addFormatters(FormatterRegistry registry) {
// ...
}
}
有关什么时候使用 FormatterRegistrar
实现的更多信息,请查看 FormatterRegistrar
SPI和FormattingConversionServiceFactoryBean
。
2.四、验证Validation
默认状况下,若是类路径上存在Bean验证(例如,Hibernate验证器),则LocalValidatorFactoryBean
注册为全局验证器,以便与@Valid和Validated
在@Controller方法参数一块儿使用。
在Java配置中,能够自定义全局Validator
实例,以下例所示:
public class WebConfig implements WebFluxConfigurer {
public Validator getValidator(); {
// ...
}
}
还能够在本地注册如下验证程序实现:
public class MyController {
protected void initBinder(WebDataBinder binder) {
binder.addValidators(new FooValidator());
}
}
若是须要在某个地方注入LocalValidatorFactoryBean
,请建立一个bean并用@Primary标记,以免与MVC配置中声明的bean冲突。
2.五、Content Type解析程序
你能够配置Spring WebFlux如何根据请求为@Controller实例肯定请求的媒体类型。默认状况下,只选中Accept
头,但也能够启用基于查询参数的策略。
如下示例显示如何自定义请求的content type解析:
public class WebConfig implements WebFluxConfigurer {
public void configureContentTypeResolver(RequestedContentTypeResolverBuilder builder) {
// ...
}
}
2.六、HTTP消息编解码器
如下示例显示如何自定义请求和响应正文的读写方式:
public class WebConfig implements WebFluxConfigurer {
public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
// ...
}
}
ServerCodecConfigurer
提供了一组默认的读卡器和写入器。你可使用它添加更多的读卡器和编写器,自定义默认的,或者彻底替换默认的。
对于jacksonjson和XML,考虑使用 Jackson2ObjectMapperBuilder
,它使用如下属性定制Jackson2ObjectMapperBuilder:
DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
被禁用。MapperFeature.DEFAULT_VIEW_INCLUSION
被禁用。
若是在类路径上检测到如下已知模块,它还会自动注册它们:
jackson-datatype-joda
: 支持Joda时间类型。jackson-datatype-jsr310
: 支持Java8日期和时间API类型。jackson-datatype-jdk8
: 支持其余Java8类型,如Optional。jackson-module-kotlin
:支持Kotlin类和数据类。
2.七、视图解析器
如下示例显示如何配置视图:
Configuration
public class WebConfig implements WebFluxConfigurer {
public void configureViewResolvers(ViewResolverRegistry registry) {
// ...
}
}
ViewResolverRegistry
为Spring框架集成的视图技术提供了快捷方式。如下示例使用FreeMarker(这还须要配置底层的FreeMarker视图技术):
public class WebConfig implements WebFluxConfigurer {
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.freeMarker();
}
// 配置Freemarker...
public FreeMarkerConfigurer freeMarkerConfigurer() {
FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
configurer.setTemplateLoaderPath("classpath:/templates");
return configurer;
}
}
还能够插入任何ViewResolver
实现,以下例所示:
public class WebConfig implements WebFluxConfigurer {
public void configureViewResolvers(ViewResolverRegistry registry) {
ViewResolver resolver = ... ;
registry.viewResolver(resolver);
}
}
为了支持内容协商和经过视图解析(除了HTML)呈现其余格式,你能够基于HttpMessageWriterView
实现配置一个或多个默认视图,该实现spring-web的任何可用编解码器。下面的示例演示如何执行此操做:
public class WebConfig implements WebFluxConfigurer {
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.freeMarker();
Jackson2JsonEncoder encoder = new Jackson2JsonEncoder();
registry.defaultViews(new HttpMessageWriterView(encoder));
}
// ...
}
2.八、静态资源
此方式提供了一种从基于资源的位置列表中提供静态资源的方便方法。
在下一个例子中,给定一个以/resources开头的请求,相对路径用于查找和服务类路径上相对于/static的静态资源。资源的有效期为一年,以确保最大限度地利用浏览器缓存并减小浏览器发出的HTTP请求。Last-Modified头也被计算,若是存在,则返回304状态代码。如下列表显示了示例:
public class WebConfig implements WebFluxConfigurer {
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**")
.addResourceLocations("/public", "classpath:/static/")
.setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS));
}
}
资源处理程序还支持一系列ResourceResolver
实现和ResourceTransformer
实现,它们可用于建立一个工具链,用于处理优化的资源。
能够根据VersionResourceResolver
或其余信息计算的MD5哈希值,对版本化的资源url使用versionresolver。ContentVersionStrategy
(md5hash)是一个很好的选择,但有一些明显的例外(好比与模块加载器一块儿使用的JavaScript资源)。
如下示例显示如何在Java配置中使用VersionResourceResolver
:
public class WebConfig implements WebFluxConfigurer {
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**")
.addResourceLocations("/public/")
.resourceChain(true)
.addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"));
}
}
可使用ResourceUrlProvider
重写URL并应用完整的解析器和转换器链(例如,插入版本)。WebFlux配置提供了一个ResourceUrlProvider
,以即可以将其注入到其余配置中。
与Spring MVC不一样,目前在WebFlux中,没有办法透明地重写静态资源url,由于没有视图技术能够利用非阻塞的解析器和转换器链。当只提供本地资源时,解决方法是直接使用ResourceUrlProvider
(例如,经过自定义元素)和block。
请注意,当同时使用EncodedResourceResolver
(例如,Gzip、Brotli encoded)和VersionedResourceResolver时,必须按顺序注册它们,以确保基于内容的版本始终基于未编码的文件可靠地计算。
WebJars也经过WebJarsResourceResolver
来支持,当org.webjars:webjars-locator-core库位于类路径上。解析器能够重写url以包含jar的版本,还能够与没有版本 - 的传入url相匹配,例如从/jquery/1.2.0/jquery.min.js至/jquery/jquery.min.js。
2.九、路径匹配
能够自定义与路径匹配相关的方式。如下示例显示如何使用PathMatchConfigurer:
public class WebConfig implements WebFluxConfigurer {
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer
.setUseCaseSensitiveMatch(true)
.setUseTrailingSlashMatch(false)
.addPathPrefix("/api",
HandlerTypePredicate.forAnnotation(RestController.class));
}
}
Spring WebFlux依赖一个名为RequestPath
的请求路径解析表示来访问解码的路径段值,去掉分号内容(即路径或矩阵变量)。这意味着,与Spring MVC不一样,您不须要指示是否解码请求路径,也不须要出于路径匹配的目的删除分号内容。Spring WebFlux也不支持后缀模式匹配,与Spring MVC不一样,建议不要依赖它。
2.十、高级配置模式
@EnableWebFlux导入DelegatingWebFluxConfiguration
配置,该配置:
为WebFlux应用程序提供默认的Spring配置
检测并委派到
WebFluxConfigurer
实现以自定义该配置。
对于高级模式,能够删除@EnableWebFlux并直接从DelegatingWebFluxConfiguration
扩展,而不是实现WebFluxConfigurer,以下例所示:
public class WebConfig extends DelegatingWebFluxConfiguration {
// ...
}
能够在WebConfig中保留现有方法,但如今还能够覆盖基类中的bean声明,而且在类路径上仍然有任意数量的其余WebMvcConfigurer
实现。
目前,Spring WebFlux不支持Netty的HTTP/2。也不支持以编程方式将资源推送到客户端。
欢迎关注和转发Spring中文社区(加微信群,能够关注后加我微信):
本文分享自微信公众号 - Spring中文社区(gh_81d233bb13a4)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。