今日分享开始啦,请大家多多指教~
RestTemplate 采纳 Java Servlet API,因而是阻塞式同步调用。相同,WebClient 是异步的,期待响应的同时不会阻塞正在执行的线程。只有在响应后果准备就绪时,才会发动告诉。
RestTemplate 依然有用武之地。非阻塞模式在某些场景下比阻塞办法占用系统资源要少得多,这时 WebClient 是一种更好的抉择。
一、WebClient
非阻塞式客户端。WebClient 应用 Spring Reactive Framework 所提供的异步非阻塞解决方案。
当 RestTemplate 为每个事件(HTTP 申请)创立一个新的线程时,WebClient 将为每个事件创立相似于“工作”。幕后,Reactive 框架将对这些“工作”进行排队,并仅在适当的响应可用时执行它们。
Reactive 框架应用事件驱动的体系结构。它提供了通过 Reactive Streams API 组合异步逻辑的办法。因而,与同步 / 阻塞办法相比,Reactive 能够应用更少的线程和系统资源来解决更多的逻辑。WebClient 是 Spring WebFlux 库的一部分。因而,咱们还能够应用晦涩的函数式 API 编写客户端代码,并将响应类型(Mono 和 Flux)作为申明来进行组合。
1. 依赖
<!--WebClient-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
2. 创立
@Test
void webclient01Create(){WebClient webClient = WebClient.create();
Mono<String> mono = webClient
.get() //get 申请
.uri(BASE_URI + "/test/get01")
.retrieve() // 获取响应体
.bodyToMono(String.class); // 格式化响应数据
log.info("{}",mono.block());
}
@Test
void webclient02Create(){WebClient webClient = WebClient.create(BASE_URI);
Mono<String> mono = webClient
.get()
.uri("/test/get01")// uri 会主动拼接
.retrieve()
.bodyToMono(String.class);
log.info("{}",mono.block());
}
@Test
void webclient03Builder(){WebClient webClient = WebClient.builder()
.baseUrl(BASE_URI)
.build();
Mono<String> mono = webClient
.get()
.uri("/test/get01")
.retrieve()
.bodyToMono(String.class);
log.info("{}",mono.block());
}
3. 应用
@Test
void webclient04(){WebClient webClient = WebClient.builder()
.baseUrl(BASE_URI)
.build();
Mono<String> mono = webClient
.get()
.uri(uriBuilder -> uriBuilder
.path("/test/get02")
.queryParam("pageSize",10)
.queryParam("pageNum",1)
.build())
.retrieve()
.bodyToMono(String.class);
log.info("{}",mono.block());
}
@Test
void webclient05(){WebClient webClient = WebClient.builder()
.baseUrl(BASE_URI)
.build();
User user = User.builder().id("0000").password("0000").build();
Mono<User> userMono = webClient
.post()
.uri(uriBuilder -> uriBuilder.path("/test/post01").build())
.contentType(MediaType.APPLICATION_JSON)
.body(Mono.just(user),User.class)
// .header()
.retrieve()
.bodyToMono(User.class);
log.info("{}",userMono.block());
}
二、RestTemplate
阻塞式客户端。RestTemplate 应用了基于每个申请对应一个线程模型(thread-per-request)的 Java Servlet API。这意味着,直到 Web 客户端收到响应之前,线程都将始终被阻塞上来。而阻塞代码带来的问题则是,每个线程都耗费了肯定的内存和 CPU 周期。这些线程将耗尽线程池或占用所有可用内存。因为频繁的 CPU 上下文(线程)切换,咱们还会遇到性能降落的问题。
1. 小技巧
办法名的第一局部示意 HTTP 申请类型,办法名的第二局部示意响应类型。例如:getForObject 示意执行 GET 申请并将响应转化成一个 Object 类型的对象。
利用 RestTemplate 封装客户端发送 HTTP 申请时,如果出现异常就会抛出 RestClientException 类型的异样;能够通过在创立 RestTemplate 对象的时候指定一个 ResponseErrorHandler 类型的异样解决类来解决这个异样
exchange 和 excute 这两个办法是通用的 HTTP 申请办法,而且这两个办法还反对额定的 HTTP 申请类型
RestTemplate 默认应用 JDK 提供的包去建设 HTTP 连贯,当然,开发者也能够应用诸如 Apache HttpComponents, Netty, and OkHttp 去建设 HTTP 连
RestTemplate 外部默认应用 HttpMessageConverter 来实现 HTTTP messages 和 POJO 之间的转换,能够通过 RestTemplate 的成员办法 tMessageConverters(java.util.List<org.springframework.http.converter.HttpMessageConverter<?>>). 去批改默认的转换器
RestTemplate 外部默认应用 SimpleClientHttpRequestFactory 创立 HTTP 连贯能够通过 HttpAccessor.setRequestFactory(org.springframework.http.client.ClientHttpRequestFactory) 去做相应的批改
每种办法都有 3 个重载办法,其中两个接管 String 类型的申请门路和响应类型、参数;另外一个接管 URI 类型的申请门路和响应类型
应用 String 类型的申请门路时,RestTemplate 会主动进行一次编码,所以为了防止反复编码问题最好应用 URI 类型的申请门路
getForObject 和 getForEntity 的区别:后者能够获取到更多的响应信息,前者这能够获取到响应体的数据
2. 示例
应用 @Configuration 提前注入的 RestTemplate
2.1GET
不带申请参数的 GET 申请
服务端代码
客户端测试代码
带申请参数的 GET 申请
服务端代码
/*
* 带申请参数的 get 申请
* @param pageSize
* @param pageNum
* @return String
*/
@GetMapping("/get02")
public String get02(@RequestParam Integer pageSize, @RequestParam Integer pageNum) {return "get02() ok, 申请参数为:" + pageSize + "/" + pageNum;
}
客户端测试代码
带门路参数的 GET 申请
服务端代码
/*
* 带门路参数的 get 申请
* @param id
* @return String
*/
@GetMapping("/get03/{id}")
public String get03(@PathVariable("id") String id) {return "get03() ok, 门路参数为:" + id;
}
客户端测试代码
带有申请参数和门路参数的 GET 申请
服务端代码
/*
* 带有申请参数和门路参数的 get 申请
* @param score
* @param id
* @return String
*/
@GetMapping("/get04/{id}")
public String get04(@RequestParam String score, @PathVariable("id") String id) {return "get04() ok, 申请参数为:" + score + ",门路参数为:" + id;
}
客户端测试代码
带申请参数的 GET 申请,Map 传递
服务端代码
/*
* 带申请参数的 get 申请
* @param pageSize
* @param pageNum
* @return String
*/
@GetMapping("/get02")
public String get02(@RequestParam Integer pageSize, @RequestParam Integer pageNum) {return "get02() ok, 申请参数为:" + pageSize + "/" + pageNum;
}
客户端测试代码
@Test
void get05(){Map<String,Object> params = new HashMap<>();
params.put("pageSize",10);
params.put("pageNum",1);
String uri = BASE_URI +"/test/get02?pageSize={pageSize}&pageNum={pageNum}";
String result = restTemplate.getForObject(uri,String.class,params);
log.info("{}",result);
}
不带申请参数的 GET 申请返回 ResponseEntity
能够获取到残缺的响应信息
服务端代码
/*
* 不带参数的 get 申请
* @return String
*/
@GetMapping("/get01")
public String get01() {return "get01() ok";
}
客户端测试代码
@Test
void get06(){
String uri = BASE_URI + "/test/get01";
ResponseEntity<String> result = restTemplate.getForEntity(uri,String.class);
log.info("{}",result);
}
携带申请参数的 GET 申请,返回 List<Entity>
服务器端代码
客户端测试代码
@Test
void get07(){String uri = BASE_URI + "/test/get05?pageSize={1}&pageNum={2}";
ResponseEntity<List> result = restTemplate.getForEntity(uri,List.class,10,1);
log.info("{}", result);
}
自定义申请头的 GET 申请
服务端代码
客户端测试代码
@Test
void get08(){String uri = BASE_URI + "/test/get05?pageSize={1}&pageNum={2}";
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("token", UUID.randomUUID().toString());
ResponseEntity<List> result = restTemplate.exchange(
uri,
HttpMethod.GET,
new HttpEntity<>(httpHeaders),
List.class,
10,1
);
log.info("{}",result);
}
2.2 POST
没有申请参数只有申请体的 POST 申请
服务端代码
/*
* 只有申请体的 POST 申请
* @param user
*/
@PostMapping("/post01")
public User post01(@RequestBody User user){log.info("申请体:{}",user.toString());
return user;
}
客户端测试代码
含有申请体、申请参数、门路参数的 POST 申请
服务端代码
/*
* 带有申请体参数、申请参数、门路参数的 POST 申请
* @param user
* @param status
* @param type
* @return String
*/
@PostMapping("/post02/{type}")
public String post02(@RequestBody User user,
@RequestParam("status") Integer status,
@PathVariable("type") String type) {return "requestBody:" + user.toString() + ",requestParam:" + status + ",
pathVariable:"+ type +"";
}
客户端测试代码
@Test
void post02() {String uri = BASE_URI + "/test/post02/++++/?status={1}";
String result = restTemplate.postForObject(
uri,
User.builder().id("***").password("....").build(),
String.class,
0
);
log.info("{}",result);
}
应用 HttpEntity 自定义申请体和申请头发起 POST 申请
服务端代码
/*
* 带有申请体参数、申请参数、门路参数的 POST 申请
* @param user
* @param status
* @param type
* @return String
*/
@PostMapping("/post02/{type}")
public String post02(@RequestBody User user,
@RequestParam("status") Integer status,
@PathVariable("type") String type) {return "requestBody:" + user.toString() + ",requestParam:" + status + ",
pathVariable:"+ type +"";
}
客户端测试代码
@Test
void post03(){String uri = BASE_URI + "/test/post02/788787/?status={1}";
User user = User.builder().id("6666").password("7777").build();
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<User> httpEntity = new HttpEntity<>(user,httpHeaders);
String result = restTemplate.postForObject(uri,httpEntity,String.class,0);
log.info("{}",result);
}
小结
在 Web 利用中咱们须要对其余服务进行 HTTP 调用。WebClient 和 RestTemplate 是两个由 Spring 提供的客户端工具。
今日份分享已完结,请大家多多包涵和指导!