Spring WebFlux 整合 MongoDB 实现 Web Service

1,999 阅读4分钟
原文链接: www.spring4all.com

使用 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 。