1、导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
2、编写接口测试webflux与传统servlet例子并通过日志打印的时间来测试两者区别
@Slf4j
@RestController
public class TestController {
@GetMapping("/webflux")
public Mono<String> webflux(){
log.info("webflux start");
Mono<String> res = Mono.fromSupplier(this::createStr);
log.info("webflux end");
return res;
}
@GetMapping("/servlet")
public String servlet(){
log.info("servlet start");
String res = createStr();
log.info("servlet end");
return res;
}
@SneakyThrows
private String createStr() {
TimeUnit.SECONDS.sleep(5);
return "hello,world";
}
}
3、Mono与Flux的区别
Flux表示的是包含0-N个元素的异步序列。Mono表示的是包含0-1个元素的异步序列,可通过修改produces属性来支持SSE服务器推送,具体如以下例子所示:
/**
* Mono 返回0-1个元素
* @return
*/
@GetMapping("/webflux")
public Mono<String> webflux(){
log.info("webflux start");
Mono<String> res = Mono.fromSupplier(this::createStr);
log.info("webflux end");
return res;
}
/**
* Flux 返回0-n个元素
* @return
*/
@GetMapping(value = "/webflux2",produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> webflux2(){
return Flux.fromStream(IntStream.range(1,10).mapToObj(i->{
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "Flux dat-"+i;
}));
}
4、使用WebClient编写远程接口调用模拟OpenFeign框架
4.1、编写调用注解及公共API接口
@Target(ElementType.TYPE)//作用范围:类、接口、 枚举
@Retention(RetentionPolicy.RUNTIME)//程序运行时获得
@Documented
public @interface ApiServer {
String value() default "";
}
@ApiServer("localhost:8080/webflux")
public interface CommonApi {
@DeleteMapping("/{id}")
Mono<Void> delete(@PathVariable("id") Long id);
@PutMapping("/update")
Mono<User> update(@RequestBody Mono<User> user);
@PostMapping("/add")
Mono<User> add(@RequestBody Mono<User> user);
@GetMapping("/{id}")
Mono<User> getUser(@PathVariable("id") Long id);
@GetMapping("/get-all")
Flux<User> getAllUser();
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Long id;
private String name;
private Integer age;
}
4.2、创建公共接口调用工厂、调用的服务器数据结构、调用的方法信息数据结构
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ServerInfo {
private String url;
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MethodInfo {
//请求url
private String url;
//请求的方法
private HttpMethod method;
//请求方法参数
private Map<String, Object> params;
//请求body
private Mono<?> body;
//是否Flux类型返回
private Boolean isFlux;
//返回的对象类型
private Class<?> returnObjectType;
}
@Component
public class CommonApiFactory implements FactoryBean<CommonApi> {
@Autowired
private ProxyCreator proxyCreator;
public CommonApiFactory(ProxyCreator proxyCreator){
this.proxyCreator=proxyCreator;
}
@Override
public CommonApi getObject() throws Exception {
return (CommonApi) proxyCreator.createProxy(this.getObjectType());
}
@Override
public Class<?> getObjectType() {
return CommonApi.class;
}
}
4.3、编写请求处理器及实现
public interface RestHandler {
/**
* 调用rest请求并返回结果
* @param methodInfo
* @return
*/
Object invokeRest(MethodInfo methodInfo);
/**
* 初始化服务器信息
* @param serverInfo
*/
void init(ServerInfo serverInfo);
}
public class WebClientRestHandler implements RestHandler {
private WebClient webClient;
/**
* 初始化webClient
*
* @param serverInfo
*/
@Override
public void init(ServerInfo serverInfo) {
this.webClient = WebClient.create(serverInfo.getUrl());
}
/**
* @param methodInfo
* @return
*/
@Override
public Object invokeRest(MethodInfo methodInfo) {
//响应数据
Object res=null;
ResponseSpec request = this.webClient.method(methodInfo.getMethod())
.uri(methodInfo.getUrl())
//接收类型
.accept(MediaType.APPLICATION_JSON)
//发起请求
.retrieve();
//判断body类型
if (methodInfo.getIsFlux()) {
res= request.bodyToFlux(methodInfo.getReturnObjectType());
} else {
res= request.bodyToMono(methodInfo.getReturnObjectType());
}
return res;
}
}
4.4、创建Jdk动态代理初始化服务器信息和调用的方法信息
public interface ProxyCreator {
Object createProxy(Class<?> type);
}
@Slf4j
@Component
public class JdkProxyCreator implements ProxyCreator {
@Override
public Object createProxy(Class<?> type) {
log.info("createProxy:" + type);
//根据接口得到API服务器信息
ServerInfo serverInfo = getServerInfo(type);
log.info("serverInfo:" + serverInfo);
RestHandler handler = new WebClientRestHandler();
//初始化服务器信息、webClient
handler.init(serverInfo);
/**
* 参数1:类加载器
* 参数2:代理的接口
* 参数3:自定义的代理方法
*/
return Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{type}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//根据方法和参数得到调用信息
MethodInfo methodInfo = getMethodInfo(method, args);
log.info("methodInfo:" + methodInfo);
//调用rest
return handler.invokeRest(methodInfo);
}
});
}
private MethodInfo getMethodInfo(Method method, Object[] args) {
MethodInfo methodInfo = new MethodInfo();
//得到请求url和请求方法
getUrlAndMethod(method, methodInfo);
//得到调用的参数和body
getParamsAndBody(method, args, methodInfo);
//得到返回的对象信息
getReturnInfo(method, methodInfo);
return methodInfo;
}
private void getReturnInfo(Method method, MethodInfo methodInfo) {
//判断返回的是flux还是mono
methodInfo.setIsFlux(method.getReturnType().isAssignableFrom(Flux.class));
//得到返回对象类型(泛型)
methodInfo.setReturnObjectType(getObjectType(method.getGenericReturnType()));
}
private Class<?> getObjectType(Type genericReturnType) {
Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
return (Class<?>) actualTypeArguments[0];
}
private void getParamsAndBody(Method method, Object[] args, MethodInfo methodInfo) {
Map<String, Object> params = new LinkedHashMap<>();
Parameter[] parameters = method.getParameters();
for (int i = 0; i < parameters.length; i++) {
//参数是否有@PathVariable
PathVariable pathVariable = parameters[i].getAnnotation(PathVariable.class);
if (pathVariable != null) {
params.put(pathVariable.value(), args[i]);
}
//参数是否有@RequestBody
RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class);
if (requestBody != null) {
methodInfo.setBody((Mono<?>) args[i]);
}
}
methodInfo.setParams(params);
}
private void getUrlAndMethod(Method method, MethodInfo methodInfo) {
for (Annotation annotation : method.getAnnotations()) {
//GET
if (annotation instanceof GetMapping) {
GetMapping a = (GetMapping) annotation;
methodInfo.setUrl(a.value()[0]);
methodInfo.setMethod(HttpMethod.GET);
}
//POST
else if (annotation instanceof PostMapping) {
PostMapping a = (PostMapping) annotation;
methodInfo.setUrl(a.value()[0]);
methodInfo.setMethod(HttpMethod.POST);
}
//DELETE
else if (annotation instanceof DeleteMapping) {
DeleteMapping a = (DeleteMapping) annotation;
methodInfo.setUrl(a.value()[0]);
methodInfo.setMethod(HttpMethod.DELETE);
}
//PUT
else if (annotation instanceof PutMapping) {
PutMapping a = (PutMapping) annotation;
methodInfo.setUrl(a.value()[0]);
methodInfo.setMethod(HttpMethod.PUT);
} else {
log.error("未知的请求类型:{}", annotation.annotationType());
}
}
}
private ServerInfo getServerInfo(Class<?> type) {
ApiServer anno = type.getAnnotation(ApiServer.class);
ServerInfo serverInfo = ServerInfo.builder().url(anno.value()).build();
return serverInfo;
}
}
5.测试
@GetMapping("/")
public void test() {
//测试信息获取是否正确
Mono<User> add = commonApi.add(Mono.just(User.builder().id(1L).name("123").age(18).build()));
Mono<User> user = commonApi.getUser(1L);
Flux<User> allUser = commonApi.getAllUser();
Mono<User> update = commonApi.update(Mono.just(User.builder().id(2L).name("123").age(18).build()));
Mono<Void> delete = commonApi.delete(1L);
}