领先一步
VMware 提供培训和认证,以加速您的进步。
了解更多祝 Spring 社区新年快乐!在新的一年里,Spring 项目生态系统将迎来令人惊叹的开发和进步,我想与大家分享一个更新的示例应用程序,该应用程序展示了我们在整个产品组合中为支持响应式编程模型所取得的一些进展。
已更新 BookStore 服务代理 示例应用程序,以演示多个 Spring 项目的集成,包括 Spring Cloud Open Service Broker、Spring Data、Spring Security、Spring HATEOAS,当然还有 Spring WebFlux 和 Spring Boot。所有这些项目都具有包含响应式支持的 GA 版本,并已准备好用于您自己的应用程序和服务中的生产环境。
为简单起见,应用程序本身充当服务代理和服务实例。虽然服务代理本身遵循 Open Service Broker API,但它们提供的服务定义更为抽象。服务可以做任何事情或成为任何东西。在本应用程序中,为每个配置的服务实例创建一组新的凭据。这些凭据用于服务实例的请求中。新服务实例的 URL 配置为与服务代理本身的路由相同。通过这种方式,凭据用于区分对各个服务实例的请求。目标是设计一个自包含的综合示例,以演示 Spring 产品组合的许多部分。
Spring Cloud Open Service Broker 是一个用于构建 Spring Boot 应用程序的框架,这些应用程序实现了 Open Service Broker API,该 API 允许开发人员向在 Cloud Foundry、Kubernetes 和 OpenShift 等云原生平台上运行的应用程序交付服务。从 3.0 版本开始,Spring Cloud Open Service Broker 通过控制器和服务接口中的响应式类型支持 Spring WebFlux 和 Spring MVC Web 框架。
要开始使用 Spring Cloud Open Service Broker,请在您的应用程序中包含 Spring Boot 启动器
implementation('org.springframework.cloud:spring-cloud-starter-open-service-broker:3.1.0.RELEASE')
接下来,实现 ServiceInstanceService
和 ServiceInstanceBindingService
。以下代码说明了所需的 API。查看示例应用程序以获取完整详细信息。
@Service
public class BookStoreServiceInstanceService
implements ServiceInstanceService {
@Override
public Mono<CreateServiceInstanceResponse> createServiceInstance(
CreateServiceInstanceRequest request) {...}
@Override
public Mono<GetServiceInstanceResponse> getServiceInstance(
GetServiceInstanceRequest request) {...}
@Override
public Mono<DeleteServiceInstanceResponse> deleteServiceInstance(
DeleteServiceInstanceRequest request) {...}
}
最初在 Spring Data 发布列车中引入了响应式支持 Spring Data Kay。Spring Data R2DBC 最近宣布发布了 GA 版本,但是 Spring Boot 还没有与 Spring Data R2DBC 集成的 GA 版本。此示例使用 MongoDB 作为后备数据存储。
要开始使用响应式 MongoDB,请添加 Spring Boot 启动器
implementation('org.springframework.boot:spring-boot-starter-data-mongodb-reactive')
出于演示目的,添加一个嵌入式 MongoDB 服务器
implementation('de.flapdoodle.embed:de.flapdoodle.embed.mongo')
接下来,配置响应式存储库
@Configuration
@EnableReactiveMongoRepositories(basePackageClasses = {
ServiceBrokerRepositoryPackageMarker.class,
WebRepositoryPackageMarker.class
})
public class ApplicationRepositoryConfiguration {
}
最后,定义一个 ReactiveCrudRepository
。以下接口是示例应用程序中的一个示例
public interface ServiceInstanceRepository extends ReactiveCrudRepository<ServiceInstance, String> {
}
最初在 Spring Security 5 中包含了响应式支持,并且与 Spring Boot 和 Spring Framework 的集成不断成熟。
要使用 Spring Security,请包含 Spring Boot 启动器
implementation('org.springframework.boot:spring-boot-starter-security')
接下来,使用 @EnableWebFluxSecurity
定义安全配置。此代码说明了一个应用程序如何同时保护 /v2
处的服务代理端点以及响应服务实例请求的 /bookstores
端点
@Configuration
@EnableWebFluxSecurity
public class SecurityConfiguration {
@Bean
public SecurityWebFilterChain securityWebFilterChain(
ServerHttpSecurity http) {
return http
.csrf().disable()
.httpBasic()
.and().authorizeExchange()
.pathMatchers("/bookstores/**").authenticated()
.pathMatchers("/v2/**").hasAuthority(
SecurityAuthorities.ADMIN)
.matchers(EndpointRequest.to("info", "health")).permitAll()
.matchers(EndpointRequest.toAnyEndpoint()).hasAuthority(
SecurityAuthorities.ADMIN)
.and().build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
接下来,实现 ReactiveUserDetailsService
@Service
public class RepositoryUserDetailsService implements
ReactiveUserDetailsService {
private final UserRepository userRepository;
public RepositoryUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
最后,WebFlux 控制器不支持权限评估器,但我们可以通过在 SpEL 表达式中调用 bean 并传递 Authentication 对象来实现类似的功能
@GetMapping("/{bookStoreId}")
@PreAuthorize("hasAnyRole('ROLE_FULL_ACCESS','ROLE_READ_ONLY') and
@bookStoreIdEvaluator.canAccessBookstore(authentication, #bookStoreId)")
public Mono<ResponseEntity<BookStoreResource>> getBooks(
@PathVariable String bookStoreId) {
return bookStoreService.getBookStore(bookStoreId)
.flatMap(this::createResponse);
}
在此示例中,解析权限以确定书店 ID 的存在情况
public boolean canAccessBookstore(Authentication authentication,
String bookStoreId) {
return authentication.getAuthorities().stream()
.filter(authority -> authority.getAuthority()
.startsWith(BOOK_STORE_ID_PREFIX))
.map(authority -> {
String serviceInstanceId = authority.getAuthority()
.substring(BOOK_STORE_ID_PREFIX.length());
return serviceInstanceId.equals(bookStoreId);
})
.findFirst()
.orElse(true);
}
Spring HATEOAS 1.0 GA 最近发布,包括用于链接创建和表示建模的响应式支持。
包含 Spring HATEOAS 启动器以激活 Spring Boot 自动配置。因为我们正在构建一个响应式 Spring WebFlux 应用程序,所以我们需要排除 Spring Web 启动器
implementation('org.springframework.boot:spring-boot-starter-hateoas') {
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-web'
}
接下来,您可以使用 WebFluxLinkBuilder
来组装超媒体资源
public Mono<BookResource> toModel(Book book, String bookStoreId) {
return Mono.just(new BookResource(book))
.flatMap(bookResource -> linkTo(methodOn(
BookController.class).getBook(bookStoreId, book.getId()))
.withSelfRel()
.toMono()
.flatMap(link -> Mono.just(bookResource.add(link)))
.thenReturn(bookResource));
}
然后,您可以将该资源用于控制器中的响应主体
return new BookStoreResourceAssembler().toModel(bookStore)
.flatMap(bookStoreResource -> Mono.just(new ResponseEntity<>(bookStoreResource, HttpStatus.OK)));
Spring Framework 5 最初在新的 Spring WebFlux Web 框架中提供了响应式支持。此外,新的 WebClient
和 WebTestClient
包括对使用和测试 Spring WebFlux 应用程序的支持。
要使用这些功能,只需包含 Spring Boot 启动器
implementation('org.springframework.boot:spring-boot-starter-webflux')
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
例如,使用 WebTestClient
验证控制器功能
this.client.get().uri("/bookstores/{bookStoreId}", bookStoreId)
.accept(MediaType.APPLICATION_JSON)
.exchange()
.expectStatus().isEqualTo(HttpStatus.OK);
Spring Boot 通过为 Spring WebFlux、Spring Data、Spring Security、Spring HATEOAS 中的响应式支持以及测试工具提供自动配置来将所有这些项目整合在一起。在许多情况下,实现只需要包含特定的 Spring Boot 启动器或相关依赖项即可激活功能。
本文简要介绍了某些 Spring 项目中的响应式支持。随着更广泛的 Spring 产品组合继续采用和支持响应式 API,开发人员将获得更多选择,可以选择在其应用程序中使用命令式或响应式样式编程。此外,此示例应用程序仅演示了现在提供响应式支持的 Spring 项目的子集。敬请期待未来更多内容!如果您对特定 Spring 项目有任何疑问或问题,请在相关项目 GitHub 页面上联系维护人员。