领先一步
VMware 提供培训和认证,助您加速进步。
了解更多Spring Data 发布列车 Fowler 的 GA 版发布标志着 6 个月开发的终点。现在是时候让您了解此次发布的内容并简要概述各项功能了。Fowler 发布列车的主要主题是性能改进和增强的 Java 8 支持,这主要体现在 Spring Data JPA 和 MongoDB 模块中,但许多其他模块也得到了显著改进。
升级到 Spring Data Fowler 版本系列最简单的方法是使用 Spring Boot 并将 spring-data-releasetrain.version 属性配置为 Fowler-RELEASE。如果您尚未开始使用 Spring Boot,请将 Spring Data BOM 添加到您的 Maven POM 的 <dependencyManagement /> 部分。
Java 8 的一个重要新特性是 Stream API,它允许 Java 开发人员定义一个操作管道,对对象流执行操作,但只有最终的操作才会实际触发对 Stream 中元素的消费。
在数据访问的上下文中,将查询执行的结果作为 Stream 提供是一个非常有用的用例,因为它可以防止查询方法的调用者在所有项都读取完毕之前阻塞。更不用说这里更高效的内存使用。
在 Fowler 版本中,我们引入了对 Java 8 Stream 作为存储库中查找方法返回类型的支持。在 MongoDB 和 JPA 模块中,您现在可以这样声明查找方法
interface CustomerRepository extends Repository<Customer, Long> {
Stream<Customer> findByLastname(String lastname);
}
调用此方法将执行支持存储库方法的查询,并在第一个结果可用时立即返回。为了在 JPA 中实现这一点,我们使用持久性提供程序特定的 API,因为 JPA 本身只提供以 List 形式获取查询结果的方法。存储库客户端现在可以在 try-with-resources 块中使用方法调用的结果。这将确保为遍历流而打开的资源最终会被关闭。
try (Stream<Customer> customers = repository.findByLastname("Matthews")) {
customers.filter(…).map(…).collect(…);
}
为了轻松持久化领域对象中的 无时区 JSR-310 类型——JDK 8 中新引入的日期/时间 API,我们在 MongoDB 和 JPA 模块中为相关类型添加了转换器。对于尚不能升级到 Java 8 的开发人员,我们为 ThreeTen Backport 项目 添加了类似的转换器集,这样即使您仍在使用 Java 7,也可以在代码中开始使用这些类型。未来切换到 Java 8 将是包名称的简单切换。
在 MongoDB 模块中,相应的 Converter 实现是自动可用的。对于 JPA,您只需向持久性提供程序注册 Jsr310JpaConverters 或 ThreeTenBackPortJpaConverters。如果您使用 LocalContainerEntityManagerFactoryBean 在 Spring 中设置 JPA 环境,只需将 org.springframework.data.jpa.convert.threeten 或 ….threetenbp 添加到要扫描的包中。对于 Spring Boot,只需将上述类添加到 @EntityScan 声明中即可
@EntityScan(
basePackageClasses = { Application.class, Jsr310JpaConverters.class }
)
@SpringBootApplication
class Application { … }
此设置将确保您的应用程序包和 Spring Data JPA 包(用于 JSR-310 转换器)都将被扫描并传递给持久性提供程序。有关完整示例,请参阅我们的 Spring Data Examples 存储库。请注意,由于转换器只是将 JSR-310 类型转换为旧版 Date 实例,因此 只支持无时区 类型(例如 LocalDateTime 等)。
Spring Data Fowler 支持最新最强大的 MongoDB 3.0 服务器版本。虽然该版本已经可以通过 MongoDB Java 驱动程序 2.13.0 版本使用,但我们也确保 Spring Data MongoDB 将与即将推出的 Java 驱动程序 3.0 版本无缝协作。因此,开发人员可以自由选择他们要使用的版本,或者何时升级到新驱动程序。有关驱动程序和服务器版本之间兼容性的通用信息,请务必查阅 MongoDB 文档。但请注意,后续开发将明确关注服务器和驱动程序的 3.0 版本线。
通常,我们鼓励每个人在 JavaConfig 中优先使用 MongoClient 而不是 Mongo,或者使用新引入的 XML 元素 <mongo:mongo-client /> 和 <mongo:client-options />。有关更多信息,请参阅参考文档。
MongoDB 引入 GeoJSON 作为处理地理结构格式已经有一段时间了。这些数据结构在类似地球的球体上操作,因此不能与 2D 索引一起使用。话虽如此,使用起来非常简单,因为我们提供了专门的类型来支持 GeoJSON。这些类型既可以在您的域类型中使用,也可以作为查询参数使用。
@Document
class Store {
@Id String id;
/**
* The location is stored in GeoJSON format:
* { "type" : "Point", "coordinates" : [ x, y ] }
*/
GeoJsonPoint location;
}
interface StoreRepository extends CrudRepository<Store, String> {
List<Store> findByLocationWithin(Polygon polygon);
}
repo.findByLocationWithin(
new GeoJsonPolygon(
new Point(-73.992514, 40.758934),
new Point(-73.961138, 40.760348),
new Point(-73.991658, 40.730006),
new Point(-73.992514, 40.758934)));
这将在 MongoDB 中创建以下查询并执行
{
"location": {
"$geoWithin": {
"$geometry": {
"type": "Polygon",
"coordinates": [[
[-73.992514,40.758934],
[-73.961138,40.760348],
[-73.991658,40.730006],
[-73.992514,40.758934]
]]
}
}
}
}
请注意,StoreRepository.findByLocationWithin(…) 仍然接受 Polygon。使用 GeoJsonPolygon 和 findByLocationWithin(…) 将创建使用 $geometry 操作符以及 GeoJSON 表示的查询。有关使用和限制的更多详细信息,请参阅 MongoDB 手册。
MongoDB 允许在服务器上执行 JavaScript 函数,方法是直接发送原始源脚本或调用先前存储的脚本。我们通过新引入的 ScriptOperations 接口公开此功能,该接口可以从 MongoOperations 获取。
ScriptOperations ops = mongoOperations.scriptOps();
ExecutableMongoScript script = new ExecutableMongoScript("function(x) { return x; }");
Object r1 = ops.execute(script, "Direct function execution.")
ops.register(new NamedMongoScript("echo", script));
Object r2 = ops.call("echo", "Call stored function.");
服务器端脚本支持将在后续版本中得到增强,我们将添加返回类型转换、用于从存储库方法调用过程的注解以及对 $where 运算符的支持。
对象到存储的映射子系统在性能改进方面进行了重大改革。我们对 Commons 和存储模块进行了分析,并在各处引入了一些缓存,与 Evans 版本系列相比,我们实际获得了令人印象深刻的每秒操作次数的增长(尽管大部分改进也已移植到 Evans 的服务版本中)。

如您所见,我们的读取访问每秒操作数增加了一倍以上,写入操作也接近此水平。
从数据存储中读取大量对象时,通过反射创建对象实例会花费大量时间。在 Fowler 发布系列中,我们引入了一个新的默认 EntityInstantiator,它通过使用 ASM 在运行时为域对象创建工厂类来解决这一瓶颈。该工厂类直接调用域类的构造函数,这比通过反射这样做要快得多。如果您对这些精巧的细节感兴趣,这里是为我们完成这项工作的类。
Redis HyperLogLog 命令提供了一种高效的解决方案,可以在不记住已遇到元素的情况下计算唯一事物。例如,这适用于按 IP 地址计算唯一页面访问量。
HyperLogLogOperations hll = redisTemplate.opsForHyperLogLog();
hll.add(today(), "8.8.8.8", "8.8.4.4");
hll.size(today()); // Unique page visits today = 2
hll.add(today(), "198.153.192.40", "8.8.8.8");
hll.size(today()); // Unique page visits today = 3
hll.size(today(), yesterday()); // Unique page visits today and yesterday
到目前为止,GemFire 模块最显著的变化是全面支持 GemFire 8。GemFire 8 自 7.0.2 以来引入了多项新变化,包括新的基于集群的配置服务。
启用该服务后,开发人员可以在 Gfsh 中记录他们的操作和类似模式的更改,例如添加区域、创建索引、配置磁盘存储等。当开发人员在集群中启动新的 GemFire 对等节点时,该成员将自动从新的基于集群的配置服务(托管在定位器中)获取其配置。
虽然 Spring Data GemFire 的基于 XML 命名空间的配置仍然是流行的选择,尤其是在高度迭代、短反馈周期的开发过程中,Spring Data GemFire 增加了对新基于集群的配置的支持,这与 Spring Data Gemfire 对 GemFire 的原生 `cache.xml 格式的支持行为类似。
要在 Spring 配置的 GemFire 节点中启用基于集群的配置,开发人员只需在 <gfe:cache /> 元素上设置 use-cluster-configuration 属性,如下所示
<gfe:cache id="gemfireCache" use-cluster-configuration="true" … />
Spring Data GemFire 将首先请求并应用集群范围的配置,然后应用 XML 命名空间特定的配置。您可以将 XML 配置元数据视为对集群配置服务发送的集群配置的增强。
有关 GemFire 新集群配置服务的更多信息,请参阅 GemFire 用户指南 和 SGF-226 以获取更多详细信息。
Spring Data REST 在 Fowler 版本中也进行了广泛的改进。最显著的改进之一是检查和使用更多实体元数据来填充响应头。例如,支持通过 @Version 注解进行乐观锁定的存储现在将获取用作 ETag 头的实体版本,以便客户端可以利用它们触发条件 GET 请求。
与此密切相关的是,使用 Spring Data 审计支持的实体将自动将其上次修改日期传播到项目资源的响应的 LastModified 头中
class Customer {
@Version Long version;
@LastModifiedDate LocalDate lastModifiedDate;
}
curl -v http://…/customers/1
Etag: 1
Last-Modified: Tue, 24 Mar 2015 12:34:56 GMT
Spring Data REST 的 Fowler 版本也提供了经过改进的 JSON Schema 支持。默认情况下,模式由 ALPS 文档中公开的表示描述符指向,该文档用于域类型。在 Starbucks 示例中,您可以看到链接的渲染方式如下
curl http://…/alps/stores
{
"version": "1.0",
"descriptors": [ {
"id": "store-representation",
"href": "https://:8080/stores/schema",
"descriptors": [ … ]
}],
…
}
点击链接将显示商店的 JSON Schema 文档
{
"title": "example.stores.Store",
"properties": {
"address": {
"$ref": "#/descriptors/address"
},
"name": {
"type": "string"
}
},
"descriptors": {
"address": {
"type": "object",
"properties": {
"zip": {
"type": "string"
},
"city": {
"type": "string"
},
"street": {
"type": "string"
},
"location": {
"$ref": "#/descriptors/point"
}
}
},
"point": {
"type": "object",
"properties": {
"x": {
"type": "number"
},
"y": {
"type": "number"
}
}
}
},
"type": "object",
"$schema": "https://schema.json.js.cn/draft-04/schema#"
}
请注意我们如何从领域类型及其 Jackson 映射中推导出模式的基本特征。所需属性可以通过使用 @JsonProperty(required = true) 来确定,日期/时间类型被正确发现并声明。您可以在 RepositoryRestConfiguration.metadataConfiguration() 上注册自定义 JSON Schema 格式或模式。
@Configuration
static class SampleConfiguration extends RepositoryRestMvcConfiguration {
@Override
protected void configureRepositoryRestConfiguration(
RepositoryRestConfiguration config) {
config.metadataConfiguration().
registerJsonSchemaFormat(JsonSchemaFormat.EMAIL, EmailAddress.class);
}
}
假设 EmailAddress 是一个您已调整 Jackson 以将其渲染为纯 String 的值对象,则此配置将导致所有 EmailAddress 类型的属性在 JSON 模式文档中以 format 设置为 email 出现。
Fowler 版本增加了对实时获取的支持,允许从索引中读取未提交的更改。SolrTemplate 上提供了 getById 方法。另外值得注意的是添加了 @Score,它受到了 Spring Data MongoDB 可用的 @TextScore 的启发。该注解允许检索文档评分,并将隐式添加所需参数,因此在检索文档查询匹配评分时不再需要显式添加 @Query(fields={"*", "score"}。
Spring Data REST 在 Evans 版本系列中发布了一个名为投影的功能。在 Fowler 版本中,我们将支持该功能的基础设施移至 Spring Data Commons,并对其进行了一些调整,以便其他项目可以在没有额外依赖的情况下使用它。该功能的核心是 ProjectionFactory,它允许您为由其他对象(例如 Map)支持的接口创建对象实例。
interface Customer {
String getFirstname();
String getLastname();
@Value("#{target.firstname + ' ' + target.lastname}")
String getFullName();
}
现在可以使用 ProjectionFactory 将此接口转换为对象。
Map<String, Object> map = new HashMap<>();
map.put("firstname", "Dave");
map.put("lastname", "Matthews");
ProjectionFactory factory = new SpelAwareProjectionFactory();
Customer customer = factory.createProjection(Customer.class, map)
assertThat(customer.getFirstname(), is("Dave"));
assertThat(customer.getLastname(), is("Matthews"));
assertThat(customer.getFullName(), is("Dave Matthews"));
如您所见,我们选择了一个 Map 来支持创建的投影实例。在底层,创建了一个 JDK 代理,它配备了方法拦截器,在 Map 支持代理的情况下,它将对访问器的调用委托给 Map 中的属性查找。使用 @Value 注解的方法将对其注解的 SpEL 表达式进行求值。如果您在 SpelAwareProjectionFactory 上配置 BeanFactory,您甚至可以在这些表达式中引用 Spring bean,从而触发更复杂的计算。
如果后台查找未返回可分配给声明的返回类型的值,则会使用标准 ConversionService 进行简单转换,然后进行递归投影步骤。
有关如何在 Spring MVC 控制器中使用投影的示例,请参阅 StackOverflow 上的此答案。
投影机制现在可以被 Spring MVC 控制器实现使用,以仅使用接口创建表单支持对象。在您的 Spring 配置中使用 @EnableSpringDataWebSupport(在 Boot 中自动激活)将解析 ProxyingHandlerMethodArgumentResolver,它将自动为接口创建代理实例并将相应的请求参数绑定到它。
interface Form {
@NotBlank String getName();
@NotBlank String getText();
}
@Controller
@RequestMapping(value = "/guestbook")
class GuestbookController {
@RequestMapping(method = RequestMethod.GET)
String guestbook(Form form, Model model) { … }
@RequestMapping(method = RequestMethod.POST)
String guestbook(@Valid Form form, Errors errors, Model model) { … }
}
请看接口如何在接受 GET 请求的方法中使用,以便为即将渲染的视图提供一个空的表单支持对象。接收 POST 请求的方法使用 Form 来指示它希望将表单数据绑定到代理实例并应用验证。
尽管这篇文章很长,但我们只是简单介绍了 Spring Data Fowler 版本的所有新功能。您可能需要浏览 发行版系列 wiki 以寻找更多精彩内容,并依次遍历指向票据和相关提交的链接,因为它们包含通常很好地演示各个功能的测试用例。
此外,前面提到的 Spring Data 示例仓库 也包含大量可供您尝试和探索的内容。