响应式书店服务代理

工程 | Roy Clarkson | January 14, 2020 | ...

迟来的新年快乐,Spring 社区的朋友们!当我们开始 Spring 项目生态系统中又一个精彩的开发和进步之年时,我想与大家分享一个更新的示例应用程序,它代表了我们在整个产品组合中为支持响应式编程模型所取得的一些进展。

BookStore Service Broker 示例应用程序已更新,以演示多个 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 3.1

Spring Cloud Open Service Broker 是一个用于构建实现 Open Service Broker API 的 Spring Boot 应用程序的框架,它允许开发人员将服务交付给在 Cloud Foundry、Kubernetes 和 OpenShift 等云原生平台上运行的应用程序。自 3.0 版本以来,Spring Cloud Open Service Broker 通过控制器和服务接口中的响应式类型支持 Spring WebFlux 和 Spring MVC Web 框架。

要开始使用 Spring Cloud Open Service Broker,请在您的应用程序中包含 Spring Boot starter

implementation('org.springframework.cloud:spring-cloud-starter-open-service-broker:3.1.0.RELEASE')

接下来,实现 ServiceInstanceServiceServiceInstanceBindingService。以下代码说明了所需的 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 Moore

Spring Data 发布列车最初在 Spring Data Kay 中引入了响应式支持。Spring Data R2DBC 最近发布了 GA 版本,但是 Spring Boot 尚未发布集成 Spring Data R2DBC 的 GA 版本。本示例使用 MongoDB 作为后端数据存储。

要开始使用 Reactive MongoDB,请添加 Spring Boot starter

implementation('org.springframework.boot:spring-boot-starter-data-mongodb-reactive')

为了演示目的,添加一个嵌入式 MongoDB 服务器

implementation('de.flapdoodle.embed:de.flapdoodle.embed.mongo')

接下来,配置 Reactive repository

@Configuration
@EnableReactiveMongoRepositories(basePackageClasses = {
		ServiceBrokerRepositoryPackageMarker.class,
		WebRepositoryPackageMarker.class
})
public class ApplicationRepositoryConfiguration {
}

最后,定义一个 ReactiveCrudRepository。以下接口是示例应用程序中的一个示例

public interface ServiceInstanceRepository extends ReactiveCrudRepository<ServiceInstance, String> {
}

Spring Security 5.2

响应式支持最初包含在 Spring Security 5 中,并且与 Spring Boot 和 Spring Framework 的集成持续成熟。

要使用 Spring Security,请包含 Spring Boot starter

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

Spring HATEOAS 1.0 GA 最近发布,其中包括对链接创建和表示模型化的响应式支持。

包含 Spring HATEOAS starter 以激活 Spring Boot 自动配置。由于我们正在构建一个响应式 Spring WebFlux 应用程序,我们需要排除 Spring Web starter

implementation('org.springframework.boot:spring-boot-starter-hateoas') {
		exclude group: 'org.springframework.boot', module: 'spring-boot-starter-web'
}

接下来,您可以使用 WebFluxLinkBuilder 组装 Hypermedia 资源

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 框架 5.2

Spring 框架 5 最初在新的 Spring WebFlux Web 框架中提供了响应式支持。此外,新的 WebClientWebTestClient 包含对使用和测试 Spring WebFlux 应用程序的支持。

要使用这些,只需包含 Spring Boot starters

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 2.2

Spring Boot 通过为 Spring WebFlux, Spring Data, Spring Security, Spring HATEOAS 中的响应式支持以及测试工具提供自动配置,将所有这些项目整合在一起。在许多情况下,实现功能只需要包含特定的 Spring Boot Starters 或相关的依赖项。

结论

本文简要介绍了部分 Spring 项目中的响应式支持。随着更广泛的 Spring 产品组合持续采用和支持响应式 API,开发人员在应用程序中选择使用命令式还是响应式编程风格将拥有更多选择。此外,此示例应用程序仅展示了目前提供响应式支持的 Spring 项目的一部分。未来还会有更多!如果您对特定的 Spring 项目有疑问或问题,请在该项目相关的 GitHub 页面上联系维护者。

获取 Spring 简报

通过 Spring 简报保持联系

订阅

提升自己

VMware 提供培训和认证,助您快速提升。

了解更多

获取支持

Tanzu Spring 通过一项简单的订阅提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件。

了解更多

即将举行的活动

查看 Spring 社区所有即将举行的活动。

查看全部