Spring WebFlux
原创大约 4 分钟
Spring WebFlux是一款专用于Web服务端的反应式编程框架。WebFlux
在Spring Framework 5.0中引入。从名字中带Flux
就能看出来,它的底层正是通过Reactor来实现Reactive Streams规范的。
需要注意的是:Spring WebFlux在官网中非常明确地不推荐盲目使用反应式编程。

因为Spring WebFlux本身并不会加快程序执行速度,但却在可以高并发情况下借助NIO
提升性能,规避I/O
阻塞带来的线程堆积。所以本质上,用Spring WebFlux开发和用SpringBoot没什么不同。
引入Spring WebFlux的依赖。
<!-- spring webflux相关依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
</dependency>
<dependency>
<groupId>dev.miku</groupId>
<artifactId>r2dbc-mysql</artifactId>
<scope>runtime</scope>
</dependency>
另外,有些人习惯于使用yaml类型的配置文件,但是当Spring WebFlux连接MongoDB时,可能会莫名其妙地抛出非法字符的异常。

看起来好像是因为认证失败。但如果仔细检查后确认配置无误,那就只有一个原因:Spring WebFlux框架无法读取yaml类型的配置文件。所以,此时就只能使用.properties类型的配置文件了。
不过如果没有出现这样的问题,那么可以直接忽略。
在Spring WebFlux的实例代码中集成了MMR
,也就是MySQL、MongoDB和Redis,基本上90%以上的开发需求都可以满足了。
整个项目的核心部分就是前面说用到的UserService
类。
首先,定义User
用户实体类。
package cn.javabook.chapter06;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.relational.core.mapping.Table;
import java.time.LocalDateTime;
/**
* 用户实体类
*
*/
@Document(collection = Constant.MONGO_COLLECTION_NAME)
@Table(value = Constant.MONGO_COLLECTION_NAME)
public class User {
@Id
private Integer id;
private String username;
private String password;
private String nickname;
private Integer gender;
private String avatar;
private LocalDateTime createtime;
private LocalDateTime updatetime;
public User(Integer id, String username, String password, String nickname, Integer gender, String avatar, LocalDateTime createtime, LocalDateTime updatetime) {
this.id = id;
this.username = username;
this.password = password;
this.nickname = nickname;
this.gender = gender;
this.avatar = avatar;
this.createtime = createtime;
this.updatetime = updatetime;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public Integer getGender() {
return gender;
}
public void setGender(Integer gender) {
this.gender = gender;
}
public String getAvatar() {
return avatar;
}
public void setAvatar(String avatar) {
this.avatar = avatar;
}
public LocalDateTime getCreatetime() {
return createtime;
}
public void setCreatetime(LocalDateTime createtime) {
this.createtime = createtime;
}
public LocalDateTime getUpdatetime() {
return updatetime;
}
public void setUpdatetime(LocalDateTime updatetime) {
this.updatetime = updatetime;
}
}
然后,创建Repository
持久化仓库类。
package cn.javabook.chapter06;
import org.springframework.data.r2dbc.repository.Query;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
import reactor.core.publisher.Mono;
/**
* 直接继承ReactiveCrudRepository
*
*/
public interface UserRepository extends ReactiveCrudRepository<User, String> {
@Query(value = "select * from user_info where id = :id")
Mono<User> findById(String id);
}
接着,编写Service
服务类。
package cn.javabook.chapter06;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
import org.springframework.data.redis.core.ReactiveStringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import javax.annotation.Resource;
/**
* 用户service
*
*/
@Service
public class UserService {
private final UserRepository userRepository;
@Resource
private ReactiveMongoTemplate reactiveMongoTemplate;
@Resource
private ReactiveStringRedisTemplate reactiveStringRedisTemplate;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public Flux<User> getUsers() {
return userRepository.findAll();
}
public Mono<User> getUserById(String id) {
return userRepository.findById(id)
.switchIfEmpty(Mono.error(new IllegalArgumentException("invalid id:" + id)));
}
public Mono<User> updateUserInfo(User user, String nickname) {
if (!StringUtils.isEmpty(nickname)) {
user.setNickname(nickname);
return userRepository.save(user);
} else {
return Mono.error(new IllegalArgumentException("invalid parameters"));
}
}
public Flux<User> getMongoUsers() {
return reactiveMongoTemplate.findAll(User.class);
}
public Mono<Object> addMongoUser(Object object, String collection) {
return reactiveMongoTemplate.insert(object, collection);
}
public Mono<String> redisGet(String key) {
return reactiveStringRedisTemplate.opsForValue().get(key);
}
public Mono<Long> redisSet(String key, String value, Long expireTime) {
return reactiveStringRedisTemplate.opsForValue().set(key, value, expireTime);
}
}
最后,完成Controller
控制器类。
package cn.javabook.chapter06;
import com.alibaba.fastjson.JSONObject;
import com.mongodb.lang.NonNull;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.time.LocalDateTime;
/**
* 用户Controller
*
*/
@RestController
public class ReactorUserController {
private final UserService userService;
public ReactorUserController(UserService userService) {
this.userService = userService;
}
@GetMapping("/mysql/users")
public Flux<User> users() {
return userService.getUsers();
}
@PutMapping("/mysql/user/{id}")
public Mono<JSONObject> update(@PathVariable("id") String id, @NonNull String nickname) {
return userService.getUserById(id)
.zipWhen(user -> userService.updateUserInfo(user, nickname), (user, userSaved) -> userSaved)
.map(user -> ResultUtil.success());
}
@GetMapping("/mongo/users")
public Flux<User> mongoUsers() {
return userService.getMongoUsers();
}
@PostMapping("/mongo/add")
public Mono<JSONObject> add(@NonNull Integer id, @NonNull String username, @NonNull String password,
@NonNull String nickname, @NonNull int gender, @NonNull String avatar) {
User user = new User(id, username, password, nickname, gender, avatar, LocalDateTime.now(), LocalDateTime.now());
Mono<Object> mono = userService.addMongoUser(user, Constant.MONGO_COLLECTION_NAME);
return mono.flatMap(result -> Mono.just(ResultUtil.success()));
}
@GetMapping("/redis/getString")
public Mono<String> redisGet(@NonNull String key) {
return userService.redisGet(key).map(result -> ResultUtil.extra(result).toJSONString());
}
@PostMapping("/redis/setString")
public Mono<Long> redisSet(@NonNull String key, @NonNull String value, @NonNull Long expireTime) {
return userService.redisSet(key, value, expireTime);
}
}
感谢支持
更多内容,请移步《超级个体》。