不久前, spring进行了较大的改动, 主要目的是为了增加对响应式编程的支持.
spring 默认是采用了reactor项目作为响应式编程(reactive programming)的支持, 我也以此作为基础来谈.
reactor项目地址: https://github.com/reactor/reactor
总的来说, reactor也是一个用于编写异步代码的库, 众所周知, 对于同步程序来说, 有IO耗时长之类的开销. 所以人们不断的推崇使用异步的方式来编写一些代码, 而java也提供了编写异步程序的方法给开发者, 那么我们为什么需要reactor. 就我短时间的使用体验来说, reactor使我们编写异步代码变得更加简单快捷, 让某项工作更加简单或让其更有效率, 我觉得就是一个库应该解决的问题, 显然reactor做到了, 在使用了reactor后, 你就再也不用写callback那种又臭又长的面条代码了, 代码的可读性与可维护性大大加强了. 相比于future, reactor又提供了更多功能齐全的操作, 编程复杂的也大大降低
好了, 我们并不是来介绍reactor的, 更多有关reactor的资料以及它与jvm其他异步方式的对比请参考reactor文档: http://projectreactor.io/docs/core/release/reference
webmvc | webflux |
---|---|
controller | handler |
request mapping | router |
* pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>cn.edu.ncugroupId>
<artifactId>reactive-demoartifactId>
<version>0.0.1-SNAPSHOTversion>
<packaging>jarpackaging>
<name>reactive-demoname>
<description>Demo project for Spring Bootdescription>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.0.0.M7version>
<relativePath/>
parent>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
<java.version>1.8java.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webfluxartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
<dependency>
<groupId>redis.clientsgroupId>
<artifactId>jedisartifactId>
<version>2.9.0version>
dependency>
<dependency>
<groupId>org.mindrotgroupId>
<artifactId>jbcryptartifactId>
<version>0.4version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>1.2.44version>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
<repositories>
<repository>
<id>spring-snapshotsid>
<name>Spring Snapshotsname>
<url>https://repo.spring.io/snapshoturl>
<snapshots>
<enabled>trueenabled>
snapshots>
repository>
<repository>
<id>spring-milestonesid>
<name>Spring Milestonesname>
<url>https://repo.spring.io/milestoneurl>
<snapshots>
<enabled>falseenabled>
snapshots>
repository>
repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-snapshotsid>
<name>Spring Snapshotsname>
<url>https://repo.spring.io/snapshoturl>
<snapshots>
<enabled>trueenabled>
snapshots>
pluginRepository>
<pluginRepository>
<id>spring-milestonesid>
<name>Spring Milestonesname>
<url>https://repo.spring.io/milestoneurl>
<snapshots>
<enabled>falseenabled>
snapshots>
pluginRepository>
pluginRepositories>
project>
package cn.edu.ncu.reactivedemo.handlers;
@Service
public class HelloWorldHandler {
public Mono helloWorld(ServerRequest request){
return ServerResponse.ok()
.contentType(MediaType.TEXT_PLAIN)
.body(BodyInserters.fromObject("hello world"));
}
}
package cn.edu.ncu.reactivedemo;
@Configuration
public class Router {
@Autowired private HelloWorldHandler helloWorldHandler;
@Autowired private UserHandler userHandler;
@Bean
public RouterFunction> routerFunction(){
return RouterFunctions.route(RequestPredicates.GET("/hello"), helloWorldHandler::helloWorld);
}
}
package cn.edu.ncu.reactivedemo;
@SpringBootApplication
public class ReactiveDemoApplication {
public static void main(String[] args) {
SpringApplication.run(ReactiveDemoApplication.class, args);
}
}
访问http://127.0.0.1:8080/hello
返回hello world表示成功
暂时支持reactive编程的数据库只有MongoDB, redis, Cassandra, Couchbase
我们直接采用redis作为测试, 做一个简陋的注册登录的接口就行了
* 配置redis
package cn.edu.ncu.reactivedemo.config;
@Configuration
public class RedisConfig {
@Autowired
private RedisConnectionFactory factory;
@Bean
public ReactiveRedisTemplate reactiveRedisTemplate(ReactiveRedisConnectionFactory connectionFactory){
return new ReactiveRedisTemplate(connectionFactory, RedisSerializationContext.string());
}
@Bean
public ReactiveRedisConnection connection(ReactiveRedisConnectionFactory connectionFactory){
return connectionFactory.getReactiveConnection();
}
public @PreDestroy void flushDb(){
factory.getConnection().flushDb();
}
}
package cn.edu.ncu.reactivedemo;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ReactiveDemoApplication.class)
public class RedisTests {
@Autowired
private ReactiveRedisConnection connection;
@Test
public void testRedis(){
connection
.stringCommands().set(ByteBuffer.wrap("h".getBytes()), ByteBuffer.wrap("w".getBytes()))
.subscribe(System.out::println);
}
}
package cn.edu.ncu.reactivedemo.handlers;
@Service
public class UserHandler {
@Autowired
private ReactiveRedisConnection connection;
public Mono register(ServerRequest request) {
Mono
package cn.edu.ncu.reactivedemo;
@Configuration
public class Router {
@Autowired private HelloWorldHandler helloWorldHandler;
@Autowired private UserHandler userHandler;
@Bean
public RouterFunction> routerFunction(){
return RouterFunctions.route(RequestPredicates.GET("/hello"), helloWorldHandler::helloWorld)
.andRoute(RequestPredicates.POST("/register"), userHandler::register)
.andRoute(RequestPredicates.POST("/login"), userHandler::login);
}
}
接口很粗糙,没有写model层, 也没有数据验证, 测试也直接用http requester进行测试了
参考:
https://spring.io/blog/2016/11/28/going-reactive-with-spring-data
http://projectreactor.io/docs/core/release/reference/
http://projectreactor.io/docs/core/release/api/
https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#webflux-fn-handler-functions
demo地址:
https://github.com/ncuwaln/spring-reactive-demo