領先一步
VMware 提供培訓和認證,以加速您的進展。
瞭解更多祝 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 是一個用於建構 Spring Boot 應用程式的框架,這些應用程式實作了 Open Service Broker API,它允許開發人員將服務交付到雲端原生平台(如 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')
接下來,實作 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 作為後端資料儲存。
若要開始使用 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 儲存庫
@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 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 GA 最近已發佈,其中包含用於連結建立和表示模型化的反應式支援。
包含 Spring HATEOAS Starter 以啟用 Spring Boot 自動配置。由於我們正在建構 Reactive Spring WebFlux 應用程式,因此我們需要排除 Spring Web Starter
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 Starter
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 Starter 或相關依賴項,即可啟用此功能。
這篇文章簡要介紹了某些 Spring 專案中的反應式支援。隨著更廣泛的 Spring 產品組合持續採用和支援反應式 API,開發人員將在應用程式中選擇使用命令式或反應式程式設計風格時獲得更多選擇。此外,此範例應用程式僅展示了現在提供反應式支援的 Spring 專案的子集。期待未來有更多!如果您對特定 Spring 專案有疑問或問題,請透過相關專案 GitHub 頁面聯絡維護人員。