领先一步
VMware 提供培训和认证,以快速提升您的进度。
了解更多基于 Spring Framework 5.3 的 Spring Data 2020.0 现已发布,其中包含大量针对各个存储库的新特性,这些特性已在各个模块中进行了介绍。在发布里程碑公告的同时,我们还希望通过一系列博客文章向您提供对新特性的更详细描述。这些文章将涵盖以下内容:
在第一部分中,我们将花时间介绍一些通用主题以及 commons 模块的某些方面,这些方面会影响特定于存储的实现。
我们从最明显的更改开始:版本控制方案。在遵循词典顺序(尊重著名的计算机科学家)超过 7 年后,我们切换到基于 CalVer 的版本控制方案。这主要会影响那些通过 `spring-data-releasetrain` 工件管理 Spring Data 工件版本的用户,该工件已更改为 `spring-data-bom` 以及新的方案。
org.springframework.data
spring-data-bom
2020.0.0
各个模块继续使用数字版本控制方案。
尽管进行了这些基础设施更改,但当前版本仍明确侧重于扩展响应式故事、更多应用程序洞察、指标以及在使用 GraalVM 原生映像时的更好开发人员体验。尽管如此,我们仍然花时间在这里和那里添加了一些零碎的东西,希望能让您的日常工作更方便一些。
这些增强功能之一是在投影中添加了对 `java.lang.Optional` 的支持。它不是一个游戏规则改变者,但如果您想避免空值,它仍然可以让您这样做。
interface User {
String getNickname();
Optional<byte[]> getPicture();
}
在处理性能洞察方面(稍后会详细介绍),我们现在将存储库初始化延迟到其首次使用,从而加快了应用程序启动时间。
我们还花时间使用 Delombok 来处理整个生产代码库,以提高可读性和调试性。
此版本为完成响应式故事的路径贡献了另一个重要部分。添加响应式上下文访问使我们能够处理诸如响应式审计和在响应式流内评估 SpEL 上下文扩展等功能。到目前为止,审计和 SpEL 在某种程度上已经可以实现。缺少的部分是访问上下文详细信息的能力,例如通常由 Spring Security 在 WebFlux 安排中提供的身份验证主体。
在上下文中,您有两种选择
审计和 SpEL 上下文扩展的命令式变体依赖于 `ThreadLocal` 存储。但是,订阅响应式管道会消除对管道将实现的线程的任何假设。因此,这种方法变得不可用。这就是第二个选项发挥作用的地方,将上下文详细信息与调用一起传递。Project Reactor 的 `Context` 功能允许将上下文数据附加到订阅。要访问上下文,需要一个 API 来使用 `Publisher` 类型。因此,在此版本中,我们引入了用于使用 `ReactiveAuditorAware` 和 `ReactiveEvaluationContextExtension` 进行审计的响应式 API 变体。
interface ReactiveAuditorAware<T> {
Mono<T> getCurrentAuditor();
}
interface ReactiveEvaluationContextExtension extends ExtensionIdAware {
Mono<? extends EvaluationContextExtension> getExtension();
}
您可以将这两个接口的实现公开为 Spring bean。Spring Data 会拾取它们并将其用于响应式审计,分别用于上下文扩展。
响应式审计将可审计实体与时间戳和当前用户(“审计员”)关联。使用 `@CreatedBy` 或 `@LastModifiedDate` 注解的属性在插入和更新时由 Spring Data 填充。在此版本之前,使用 `@CreatedDate` 和 `@LastModifiedDate` 注解的属性是通过实体回调更新的。从此版本开始,审计员将通过使用专用实体回调从 `ReactiveAuditorAware` 传播到实体。
使用 Spring Security 的 `ReactiveSecurityContextHolder` 的 `ReactiveAuditorAware` 的示例实现可能如下所示
class ReactiveUsernameAuditor implements ReactiveAuditorAware<String>{
@Override
public Mono<String> getCurrentAuditor() {
return ReactiveSecurityContextHolder.getContext()
.map(SecurityContext::getAuthentication)
.map(Authentication::getPrincipal)
.map(Object::toString);
}
}
您需要两个步骤才能为您的存储模块启用响应式审计。首先,通过特定于存储的 `@EnableReactive…Auditing` 注解启用响应式审计。其次,注册一个 `ReactiveAuditorAware` bean。Cassandra、Elasticsearch、MongoDB、Neo4j 和 R2DBC 支持响应式审计。
以下配置片段显示了如何启用响应式审计。您可以在 Github 上找到 完整示例。
@Configuration
@EnableReactiveCassandraAuditing
class ApplicationConfiguration {
@Bean
ReactiveAuditorAware<String> reactiveAuditorAware() {
return () -> Mono.just("the-static-auditor-name");
}
}
请注意,该示例导致名为 `the-static-auditor-name` 的静态审计员。使用从上下文中派生的动态审计员需要其他组件,例如 Spring Security 和 WebFlux。请注意,以前有几个存储库通过 `@Enable…Auditing` 启用了响应式审计的日期部分。随着 `@EnableReactive…Auditing` 的引入,情况不再如此,`@Enable…Auditing` 仅启用命令式审计。如果您需要响应式审计,请确保相应地调整您的配置。
SpEL 上下文扩展代表一个 SPI,允许插入应用程序或库特定的扩展,这些扩展提供可以通过使用 SpEL 表达式在不同位置使用的功能。上下文扩展可以保存特定于应用程序的状态、公开特定于域的功能或访问与传入应用程序请求关联的上下文数据。SpEL 上下文功能的一个典型示例是访问与经过身份验证或匿名应用程序用户发出的 HTTP 请求关联的安全上下文。
与响应式审计类似,任何每个请求的上下文都需要从订阅者 `Context` 中检索。在此版本中,我们引入了 `ReactiveEvaluationContextProvider`,它是 `EvaluationContextProvider` 的响应式变体。`ReactiveEvaluationContextProvider` 允许延迟 `EvaluationContext` 检索。通过返回 `Mono`,上下文提供程序可以访问订阅者 `Context`。响应式上下文扩展需要实现 `ReactiveEvaluationContextExtension`,以便它们可以参与延迟上下文扩展解析。响应式扩展可以提取上下文数据并将其传递给实际的 `EvaluationContextExtension`,该扩展向 Spring Data 的 SpEL 评估机制提供功能。
请注意,响应式 SpEL 支持仅适用于响应式存储库查询方法。其他组件中的 SpEL 表达式(例如,MongoDB `@Document` 中的集合名称或 Cassandra `@Table` 名称)在解析时不会考虑响应式上下文扩展,因为该 API 无法访问订阅者 `Context`。
自成立以来,如果查询包含至少一个 SpEL 表达式,Spring Data 就会尝试在 SpEL 表达式处理之前解析所有已注册的 SpEL 上下文扩展。上下文解析可能很昂贵。此外,有时上下文可能不存在。考虑以下查询方法
@Query("SELECT * FROM person WHERE tenant = :#{tenantId}")
List<Person> findAllPeopleForTenant();
@Query("SELECT * FROM person WHERE tenant = :#{tenantId} or 1=?#{hasRole('ROLE_ADMIN') ? 1 : 0")
List<Person> findAllPeople();
两种查询方法都使用 SpEL 表达式,第二种查询方法引用了 Spring Security 的 SpEL 上下文扩展。当调用只考虑 `tenantId` 的第一种方法(`findAllPeopleForTenant`)时,Spring Data 也加载了 Spring Security 的扩展。如果在安全上下文之外调用该方法,则方法调用会失败,因为 Spring Security 无法解析安全上下文。在 `CommandLineRunner`、`ApplicationEventLister` 或生命周期方法(例如 `@PostConstruct`)中的调用是典型的没有安全上下文的示例。
class MyComponent {
@Autowired PersonRepository repository;
@PostConstruct
public void postConstruct() {
TenantIdHolder.setCurrentTenant(4711);
// this method fails with an exception because it attempts to resolve
// the security context although it's not required in for the actual query.
repository.findAllPeopleForTenant();
}
}
随着 Reactive SpEL 上下文扩展的引入,我们改进了我们的 SPI,只解析实际需要的扩展。`org.springframework.data.spel.ExpressionDependencies` 对 SpEL 表达式进行静态分析,并收集符号(属性引用和方法调用)。通过 `EvaluationContextProvider` 获取 `EvaluationContext` 会接受 `ExpressionDependencies` 来检查哪些扩展提供这些符号,并且只加载能够满足依赖项要求的扩展。此更改对于大多数应用程序来说应该是透明的,因为它只涉及内部代码。请注意,依赖项分析允许安排在例如 `@PostConstruct` 中运行支持 SpEL 的查询,如果查询只引用可以解析的上下文功能。不参与查询的上下文扩展不再尝试解析,并且该更改可能会反映到您的应用程序中。此优化适用于命令式和反应式代码路径,并且应该会提高性能。
深入了解正在运行的应用程序对于某些业务至关重要。持久层(尤其是)可能是由于网络或服务器端缓慢的查询执行而导致性能问题的罪魁祸首。仓库指标现在通过直接连接到 Spring Data 执行基础设施,为您提供数据访问层运行时性能的可靠来源。附加到仓库的监听器会收到对接口的每次调用的通知,提供有关仓库本身、调用的方法、传递的参数以及执行结果(成功、错误和取消信号)的信息,并忽略实际结果。
对于同步仓库接口,测量从查询方法调用时间开始,包括实际解析潜在的带注释查询和评估通过 SpEL 表达式提供值的扩展所需的时间,如上一节所述,并在返回潜在的转换结果时结束。
对于反应式仓库,实际订阅标志着调用入口点,而完成或取消信号则完成测量。
目前,设置不是很方便,因为它需要一个 `BeanPostProcessor` 来通过添加调用监听器来操作仓库工厂 bean。但是,未来的 Spring Boot 版本将提供对包含在执行器端点中的仓库指标的专用支持。
class RepositoryMetricsPostProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) {
if (bean instanceof RepositoryFactoryBeanSupport) {
RepositoryFactoryBeanSupport<?, ?, ?> repositoryFactoryBean = (...) bean;
repositoryFactoryBean.addRepositoryFactoryCustomizer(repositoryFactory -> {
repositoryFactory.addInvocationListener(System.out::println);
});
}
return bean;
}
}
为了让您看到它的实际运行情况,我们为您准备了一个小型指标示例。它会将调用持续时间打印到控制台。
PersonRepository.save(Object): 2 ms – SUCCESS
PersonRepository.save(Object): 2 ms – SUCCESS
PersonRepository.findAll(): 32 ms – SUCCESS
PersonRespository.findByName(String): 1 ms - SUCCESS
说到指标和性能,人们很快就会想到零扩展场景、启动速度和GraalVM 原生镜像。Spring Data 团队一直在努力推进这方面的工作,从而在编译使用 Spring Data 仓库的原生镜像时,提高了开发人员的体验。这些工作包括用于禁用某些功能的简单切换(例如,用于优化实体实例化场景的代码生成)、编译提示以及作为spring-graalvm-native项目一部分的 `SpringDataComponentProcessor`。组件处理器检查 Spring Data 仓库、域类型和方法签名。根据这些信息,它将所有必需的代理、资源和反射配置添加到原生镜像中以使其工作。这也包括任何特定于存储的注释和命令式和反应式接口的自定义实现。如果您好奇,请尝试一下试试看!
至此,我们完成了本系列的第一部分。请关注此空间以获取与您最喜欢的存储相关的更新,并查看Spring Data 示例以缩短发布时间。