使用 Spring WebFlux、SpringData 和MongoDB 创建一个响应的 RESTful Web 服务
了解如何在本教程中为 Spring WebFlux、Spring Data 和 MongoDB一步步创建一个响应式的 RESTful web 服务。
这篇文章的基础是《从 Spring 框架开始》 第四版。本文的源代码可以在 ch19-tor3-webservice 项目中找到。(bit.ly/2zTuD0Y) 要运行这个项目,可以在 Tomcat 9 上部署 ch19-reptor3-webservice 项目,并执行 ReactiveWebClient 的主要方法(位于 src/test/java 文件夹中)。
要创建一个响应的 RESTful web 服务,您需要确保 web 服务的每个层(数据访问、服务和 web )在本质上都是响应的。
使用 Spring Data 开发数据访问层。
对于 MongoDB 来说,可以使用动态数据库驱动程序,可以使用 Spring Data (Kay release )来重新与 MongoDB 数据库交互。BankAccountReactorRepository (一个 Spring 数据存储库),它定义了返回响应类型的方法(由反应器定义):
public interface BankAccountReactorRepository extends ReactiveMongoRepository<BankAccountDetails, String>, BankAccountReactorRepositoryCustom {
Mono<Long> countByBalance(int balance);
Flux<BankAccountDetails> findByBalance(int balance);
.....
}
注意 — 从存储库方法返回反应类型( Flux 和 Mono ),也可以返回由 RxJava 2 定义的反应类型。
MongoDB配置Spring Data
import org.springframework.data.mongodb.ReactiveMongoDatabaseFactory;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
import org.springframework.data.mongodb.core.SimpleReactiveMongoDatabaseFactory;
import org.springframework.data.mongodb.repository.config.EnableReactiveMongoRepositories;
.....
@Configuration
@EnableReactiveMongoRepositories(basePackages = "sample.spring.chapter19.bankapp.repository")
public class DatabaseConfig {
@Bean
public MongoClient mongoClient() throws UnknownHostException {
return MongoClients.create("mongodb://localhost");
}
public ReactiveMongoDatabaseFactory mongoDbFactory() .. {
return new SimpleReactiveMongoDatabaseFactory(mongoClient(),
"test");
}
@Bean
public ReactiveMongoTemplate reactiveMongoTemplate() .. {
return new ReactiveMongoTemplate(mongoDbFactory());
}
}
@EnableReactiveMongoRepositories 注释允许使用响应式 MongoDB 存储库。basepackage 属性指定要扫描无反应 MongoDB 存储库的包。
@Bean 注解的 mongoDbFactory 方法创建并返回SimpleReactiveMongoDatabaseFactory 的一个实例。SimpleReactiveMongoDatabaseFactory 的构造函数接受 MongoClient 的实例和数据库的名称(在我们的例子中是测试)。
@Bean 注释的 reactiveMongoTemplate 方法配置了一个Spring Data MongoDB的 reactiveMongoTemplate 实例,该模板被存储库用于在 MongoDB 上执行被动操作。
开发服务层
由于我们不希望服务层中的方法阻塞,所以服务方法返回响应的类型。下面的清单显示了定义服务方法的 BankAccountService 接口:
public interface BankAccountService {
Mono<BankAccountDetails> saveBankAccount(BankAccountDetails bankAccountDetails);
Flux<BankAccountDetails> findByBalance(int balance);
Mono<Void> addFixedDeposit(String bankAccountId, int amount);
.....
}
以下清单显示实现 BankAccountService 接口的 BankAccountServiceImpl 类:
@Service
public class BankAccountServiceImpl implements BankAccountService {
@Autowired
private BankAccountReactorRepository bankAccountRepository;
.....
@Override
public Mono<Long> countByBalance(int balance) {
return bankAccountRepository.countByBalance(balance);
}
@Override
public Flux<BankAccountDetails> findByBalance(int balance) {
return bankAccountRepository.findByBalance(balance);
}
.....
}
countByBalance 和 findByBalance 方法调用 BankAccountReactorRepository 中定义的相应方法。
使用Spring WebFlux 开发Web层
Spring WebFlux 模块(在 Spring 5中引入)支持开发响应式 web 应用程序和基于RESTful web 服务。就像 Spring Web MVC的情况一样,您可以使用
@Controller、@GetMapping 等来编写响应式的Web控制器。
下面的清单显示了 BankAccountController 类(一个响应式的web控制器),它调用 BankAccountService 的方法:
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
.....
@RestController
@RequestMapping("/bankaccount")
public class BankAccountController {
@Autowired
private BankAccountService bankAccountService;
.....
@GetMapping("/countByBalance/{balance}")
public Mono<Long> countByBalance(@PathVariable("balance") int balance) {
return bankAccountService.countByBalance(balance);
}
@GetMapping("/findByBalance/{balance}")
public Flux<BankAccountDetails> findByBalance(@PathVariable("balance") int balance) {
return bankAccountService.findByBalance(balance);
}
.....
}
配置Spring WebFlux
下面的清单展示了配置 WebFlux 的 WebConfig 类
import org.springframework.web.reactive.config.EnableWebFlux;
.....
@EnableWebFlux
@Configuration
@ComponentScan(basePackages = "sample.spring.chapter19.bankapp.controller")
public class WebConfig { }
在上面的清单中,@EnableWebFlux 注解为项目配置 WebFlux 。@ComponentScan 指定包含特定于 web 层的类的包。作为控制器在sample.spring.chapter19.bankapp 中定义。控制器包,它被指定为@ComponentScan 注解的 basePackages 属性的值。
ServletContext 配置
您可以通过使用Spring的AbstractAnnotationConfigDispatcherHandlerInitializer类以编程方式配置基于webflux的web应用程序(或RESTful web服务)的ServletContext,如下所示:
import .....web.reactive.support.AbstractAnnotationConfigDispatcherHandlerInitializer;
.....
public class BankAppInitializer extends
AbstractAnnotationConfigDispatcherHandlerInitializer {
@Override
protected Class<?>[] getConfigClasses() {
return new Class[] { WebConfig.class,
DatabaseConfig.class, BankAccountServiceImpl.class };
}
}
getConfigClasses 方法返回我们想要在应用程序上下文中注册的 @Configuration(或@Component) WebConfig.class 在web层注册bean , DatabaseConfig.class 在数据访问层中注册bean。
测试响应的RESTful Web 服务
Spring的WebClient类(不像RestTemplate)允许您重新与一个响应式的RESTful web服务交互。下面的清单显示了访问BankAccountController定义的方法的ReactiveWebClient类:
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
.....
public class ReactiveWebClient {
private static Logger logger =
LogManager.getLogger(ReactiveWebClient.class);
private static WebClient webClient =
WebClient.create("http://localhost:8080/
ch19-reactor3-webservice/bankaccount");
public static void main(String args[]) throws InterruptedException {
// --find BankAccountDetails entities with balance 1000
webClient.get().uri("/findByBalance/{balance}",
1000).accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToFlux(BankAccountDetails.class)
.subscribe(account -> logger.info("account with balance 1000 -> " + account.getAccountId()));
}
}
WebClient的 create 方法创建了一个带有基本URL、主机和端口信息的WebClient实例。在端口8080上本地部署了ch19- response -webservice, BankAccountController被映射到/bankaccount请求路径,下面的URL被传递给创建方法http://localhost:8080/ch19-tor3 -webservice/bankaccount。
retrieve 方法发送HTTP请求并检索响应主体
bodyToFlux方法将响应体提取到 Flux。当BankAccountController的findByBalance方法返回Flux类型时,调用bodyToFlux (BankAccountDetails.class)方法将响应主体转换为Flux 。