Spring 框架入门教程
1. 1. Spring 框架 2. 2. Spring 5 3. 3. Spring WebFlux 4. 4. 先介绍一下 Spring 5. 5. 什么是 Spring 依赖注入 6. 6. 什么是 Spring IoC 容器 和 Bean 7. 7. Spring Bean 的生命周期 8. 8. Spring REST 开发 9. 9. Spring REST XML 10. 10. Spring RestTemplate 开发 11. 11. Spring AOP 切面编程 12. 12. Spring AOP 方法调优 13. 13. Spring 注解详解 14. 14. Spring 核心注解之 @Autowired 15. 15. Spring 核心注解之 @RequestMapping 16. 16. Spring MVC 开发样例 17. 17. Spring MVC 开发指南 18. 18. Spring MVC 异常处理机制 19. 19. Spring MVC Validator 20. 20. Spring MVC 拦截器 21. 21. Spring MVC 文件上传 22. 22. Spring MVC 国际化(i18n) 23. 23. Spring MVC Hibernate MqSQL 24. 24. Spring ORM 25. 25. Spring ORM JPA 26. 26. Spring Data JPA 27. 27. Spring 事务管理 28. 28. 常用的 Spring JdbcTemplate 29. 29. Spring Security 简介 30. 30. Spring Security 教程 31. 31. Spring Security UserDetailsService 32. 32. Spring MVC 登录注销简单案例 33. 33. Spring Security Roles 34. 34. Spring Boot Tutorial 35. 35. Spring Boot Components 36. 36. Spring Boot CLI Hello World 37. 37. Spring Boot Initilizr Web 38. 38. Spring Boot Initilizr IDE 39. 39. Spring Boot Initilizr CLI 40. 40. Spring Boot Initilizr Tools 41. 41. Spring Boot MongoDB 42. 42. Spring Boot Redis Cache 43. 43. Spring Boot 常见面试问题 44. 44. Spring Batch 45. 45. Spring Batch 批处理示例 46. 46. Spring AMQP 47. 47. Spring RabbitMQ 48. 48. Spring AMQP RabbitMQ 49. 49. Apache ActiveMQ 安装与启动 50. 50. Spring ActiveMQ 教程 51. 51. Spring ActiveMQ 示例 52. 52. Spring JDBC 53. 53. Spring DataSource JNDI 54. 54. Spring Hibernate 55. 55. Spring Primefaces JPA 56. 56. Spring Primefaces MongoDB 57. 57. Spring Primefaces Hibernate 58. 58. SpringRoo Primefaces Hibernate 59. 59. Spring JSF 60. 60. Spring JDF Hibernate 61. 61. Spring Data MongoDB 62. 62. Spring 常见面试问题

3. Spring WebFlux

Spring Web Flux是spring5中引入的新模块。Spring Web Flux是Spring框架中实现反应式编程模型的第一步。

Spring反应式编程

如果您是反应式编程模型的新手,那么我强烈建议您阅读以下文章来了解反应式编程。

如果你是第五春的新手,请仔细阅读Spring5特色.

SpringWebFlux

SpringWebFlux是SpringMVC模块。Spring Web Flux用于创建基于事件循环执行模型的完全异步和非阻塞应用程序。

下图来自Spring官方文档,提供了SpringWebFlux和SpringWebMVC的比较。

如果您希望在非阻塞反应式模型上开发web应用程序或restweb服务,那么您可以看看Spring Web Flux。

Spring Web Flux在Tomcat、Jetty、servlet3.1+容器以及Netty和Undertow等非Servlet运行时都受支持。

Spring Web Flux是基于项目反应堆. 项目反应器是实现反应流规范。Reactor提供两种类型:

  1. 单声道:实现Publisher并返回0或1个元素
  2. 通量:实现Publisher并返回N个元素。

Spring Web Flux Hello World示例

让我们构建一个简单的Spring Web Flux Hello World应用程序。我们将创建一个简单的restweb服务,并使用Spring Boot在默认Netty服务器上运行它。

我们最终的项目结构如下图所示。

让我们逐一查看应用程序的每个组件。

Spring Web Flux-Maven依赖项


<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.journaldev.spring</groupId>
  <artifactId>SpringWebflux</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>Spring WebFlux</name>
  <description>Spring WebFlux Example</description>
  
      <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <jdk.version>1.9</jdk.version>
    </properties>
    
  <parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.1.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-webflux</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>io.projectreactor</groupId>
			<artifactId>reactor-test</artifactId>
			<scope>test</scope>
		</dependency>
    </dependencies>
	<repositories>
		<repository>
			<id>spring-snapshots</id>
			<name>Spring Snapshots</name>
			<url>https://repo.spring.io/snapshot</url>
			<snapshots>
				<enabled>true</enabled>
			</snapshots>
		</repository>
		<repository>
			<id>spring-milestones</id>
			<name>Spring Milestones</name>
			<url>https://repo.spring.io/milestone</url>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</repository>
	</repositories>
	<pluginRepositories>
		<pluginRepository>
			<id>spring-snapshots</id>
			<name>Spring Snapshots</name>
			<url>https://repo.spring.io/snapshot</url>
			<snapshots>
				<enabled>true</enabled>
			</snapshots>
		</pluginRepository>
		<pluginRepository>
			<id>spring-milestones</id>
			<name>Spring Milestones</name>
			<url>https://repo.spring.io/milestone</url>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</pluginRepository>
	</pluginRepositories>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.7.0</version>
                    <configuration>
                        <source>${jdk.version}</source>
                        <target>${jdk.version}</target>
                    </configuration>
                </plugin>
            </plugins>
    </pluginManagement>
    </build>
    
</project>

最重要的依赖关系是spring-boot-starter-webflux and spring-boot-starter-parent. Some other dependencies are for creating JUnit test cases.

Spring WebFlux处理程序

Spring Web Flux处理程序方法处理请求并返回Mono or Flux as response.


package com.journaldev.spring.component;

import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;

import reactor.core.publisher.Mono;

@Component
public class HelloWorldHandler {

	public Mono<ServerResponse> helloWorld(ServerRequest request) {
		return ServerResponse.ok().contentType(MediaType.TEXT_PLAIN)
			.body(BodyInserters.fromObject("Hello World!"));
	}
}

注意反应性成分Mono holds the ServerResponse body. Also look at the function chain to set the return content type, response code and body.

Spring WebFlux路由器

路由器方法用于定义应用程序的路由。这些方法返回RouterFunction object that also holds ServerResponse body.


package com.journaldev.spring.component;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;

@Configuration
public class HelloWorldRouter {

	@Bean
	public RouterFunction<ServerResponse> routeHelloWorld(HelloWorldHandler helloWorldHandler) {

		return RouterFunctions.route(RequestPredicates.GET("/helloWorld")
                .and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), helloWorldHandler::helloWorld);
	}
}

所以我们公开了一个GET方法/helloWorld and the client call should accept plain text response.

Spring引导应用程序

让我们用Spring Boot配置我们的简单WebFlux应用程序。


package com.journaldev.spring;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}
}

如果您看一下上面的代码,就会发现与Spring Web Flux无关。但是Spring Boot将把我们的应用程序配置为Spring Web Flux,因为我们添加了spring-boot-starter-webflux module.

Java 9模块支持

我们的应用程序已经准备好在Java8上执行了,但是如果您使用的是Java9,那么我们还需要添加module-info.java class.


module com.journaldev.spring {
    requires reactor.core;
    requires spring.web;
    requires spring.beans;
    requires spring.context;
    requires spring.webflux;
    requires spring.boot;
    requires spring.boot.autoconfigure;
    exports com.journaldev.spring;
}

运行Spring Web Flux Spring引导应用程序

如果您在Eclipse中支持Spring,那么您可以作为Spring Boot应用程序来运行上面的类。

如果您喜欢使用命令行,那么打开终端并运行命令mvn spring-boot:run from the project source directory.

一旦应用程序运行,请注意以下日志消息,以确保我们的应用程序一切正常。当您通过添加更多路由和功能来扩展这个简单的应用程序时,它也很有帮助。


2018-05-07 15:01:47.893  INFO 25158 --- [           main] o.s.w.r.f.s.s.RouterFunctionMapping      : Mapped ((GET && /helloWorld) && Accept: ) -> com.journaldev.spring.component.HelloWorldRouter$$Lambda$501/704766954@6eeb5d56
2018-05-07 15:01:48.495  INFO 25158 --- [ctor-http-nio-1] r.ipc.netty.tcp.BlockingNettyContext     : Started HttpServer on /0:0:0:0:0:0:0:0:8080
2018-05-07 15:01:48.495  INFO 25158 --- [           main] o.s.b.web.embedded.netty.NettyWebServer  : Netty started on port(s): 8080
2018-05-07 15:01:48.501  INFO 25158 --- [           main] com.journaldev.spring.Application        : Started Application in 1.86 seconds (JVM running for 5.542)

从日志中可以清楚地看到,我们的应用程序运行在Netty服务器上的端口8080上。让我们继续测试我们的应用程序。

SpringWebFlux应用测试

我们可以用各种方法测试我们的应用程序。

  1. 使用CURL命令
    
    $ curl https://localhost:8080/helloWorld
    Hello World!
    $ 
    
  2. 在浏览器中启动URL
  3. 使用spring5中的WebTestClient

    下面是一个JUnit测试程序,它使用WebTestClient from Spring 5 reactive web.

    
    package com.journaldev.spring;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.http.MediaType;
    import org.springframework.test.context.junit4.SpringRunner;
    import org.springframework.test.web.reactive.server.WebTestClient;
    
    @RunWith(SpringRunner.class)
    @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
    public class SpringWebFluxTest {
    
    	@Autowired
    	private WebTestClient webTestClient;
    
    	
    	@Test
    	public void testHelloWorld() {
    		webTestClient
    		.get().uri("/helloWorld") // GET method and URI
    		.accept(MediaType.TEXT_PLAIN) //setting ACCEPT-Content
    		.exchange() //gives access to response
    		.expectStatus().isOk() //checking if response is OK
    		.expectBody(String.class).isEqualTo("Hello World!"); // checking for response type and message
    	}
    
    }
    

    运行它一个JUnit测试用例,它应该会通过。

  4. 使用springwebreactive的WebClient

    我们也可以使用WebClient to call the REST web服务.

    
    package com.journaldev.spring.client;
    
    import org.springframework.http.MediaType;
    import org.springframework.web.reactive.function.client.ClientResponse;
    import org.springframework.web.reactive.function.client.WebClient;
    
    import reactor.core.publisher.Mono;
    
    public class HelloWorldWebClient {
    
    	public static void main(String args[]) {
    		WebClient client = WebClient.create("https://localhost:8080");
    
    		Mono<ClientResponse> result = client.get()
    				.uri("/helloWorld")
    				.accept(MediaType.TEXT_PLAIN)
    				.exchange();
    
    			System.out.println("Result = " + result.flatMap(res -> res.bodyToMono(String.class)).block());
    	}
    	
    }
    

    只要将它作为一个简单的java应用程序运行,您就会看到包含大量调试消息的正确输出。

摘要

在这篇文章中,我们了解了Spring Web Flux以及如何构建helloworld反应式restfulweb服务。

很高兴看到像Spring这样的流行框架支持反应式编程模型。但是我们有很多内容需要讨论,因为如果您的所有依赖项都不是被动的和非阻塞的,那么您的应用程序也不是真正的被动的。

例如,关系数据库供应商没有反应式驱动程序,因为它们依赖于JDBC,这不是被动的。因此hibernateapi也是非反应性的。因此,如果您使用的是关系数据库,那么您还不能构建一个真正的反应式应用程序。我希望它迟早会改变。

你可以从我的GitHub存储库.

参考文献:官方文件