====================================
相关的文章
====================================
SpringBoot系列: 与Spring Rest服务交互数据
https://www.cnblogs.com/harrychinese/p/Springboot_SpringRest.html

SpringBoot系列: Spring MVC视图方法的补充
https://www.cnblogs.com/harrychinese/p/springboot_mvc_view_function2.html

[转载]SpringBoot系列: SpringMVC 参数绑定注解解析
https://www.cnblogs.com/harrychinese/p/springboot_mvc_view_function.html

SpringBoot系列: RestTemplate 快速入门
https://www.cnblogs.com/harrychinese/p/springboot_resttemplate.html

====================================
微服务进程间通信
====================================

微服务进程之间的通讯有 http 和 rpc 两种协议, 在 Spring Cloud 项目中一般都以 http 通信, 常用的访问框架有:
1. JdkHttpConnection 组件
2. Apache HttpClient 组件
3. RestTemplate (Spring Framework 提供的 webclient, 缺省是基于 JdkHttpConnection 实现的, 也可以基于 Apache HttpClient 、 OkHttp 实现)
4. Feign (spring-cloud-starter-feign 项目提供的 webclient)
5. OkHttp (Square 开源的 http 客户端)
6. AsyncHttpClient(基于 Netty 的 http 客户端)
7. Retrofit (Square 开源的 http 客户端, 对于 OkHttp 做了封装)

JdkHttpConnection/Apache HttpClient 等 web 客户端是底层客户端, 如果直接在微服务项目中使用, 需要处理很多工作. 其他几个客户端都针对 Rest 服务做了很多封装, 这包括:
1. 连接池
2. 超时设置
3. 请求和响应的编码/解码 (json <-> pojo)
4. 支持异步

因为我们开发的项目是基于 Spring Boot 的, 考虑到集成性和 Spring 官方的支持程度, 自然选择 RestTemplate 或 Feign 了.
有关 http 通信经常会看到 Robin 相关资料, 该技术是 Spring Cloud Netflix 的一个项目, 是一个基于 Http 和 Tcp 的客户端负载均衡器, 支持两种策略 Round robin 或 weigh based. Robin 可以和 RestTemplate/Feign 搭配使用, 为 web 请求提供负载均衡特性.

==========================
pom.xml
==========================
RestTemplate 默认使用 jackson 完成 json 序列化和反序列化.

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>

==========================
RestTemplate 实例化
==========================
RestTemplate 实例最好是由 Spring 容器管理, 而不是在用到时候 new RestTemplate() 一个实例.
可以在 @Controller/@Service/@Configuration 类中, 声明一个 restTemplate bean, 其他地方直接注入即可使用.

@RestController
class HelloController {
//声明 bean
@Bean
@LoadBalanced //增加 load balance 特性.
public RestTemplate restTemplate() {
return new RestTemplate();
} //注入
@Autowired
private RestTemplate restTemplate; private void someMethod(){
//使用 restTemplate
}
}

或者, 先注入 RestTemplateBuilder, 然后通过该 builder 来构建 RestTemplate 实例. 使用 builder 可以为 RestTemplate 定制化东西:
builder.additionalInterceptors() 方法: 可以通过增加拦截器为每次 request 记录 log,
builder.additionalMessageConverters() 方法: 比如增加 MessageConverter 实现特定的 json <-> pojo 的转换,

@RestController
class Hello2Controller {
//注入 RestTemplateBuilder
@Autowired
private void initRestTemplate(RestTemplateBuilder builder){
this.restTemplate=builder.build();
} private RestTemplate restTemplate; private void someMethod(){
//使用 restTemplate
}
}

==========================
RestTemplate 使用
==========================
RestTemplate 主要方法

Http 方法 | RestTemplate 方法
DELETE | delete
GET | getForObject(), getForEntity()
HEAD | headForHeaders()
OPTIONS | OptionsForAllow()
PUT | put
any | exchange(), execute()

1. delete() 方法, 在 url 资源执行 http DELETE 操作.
2. exchange() 方法, 通用的 web 请求方法, 返回一个 ResponseEntity 对象, 这个对象是从响应体映射而来. 该方法支持多种 web method, 是其他 RestTemplate 方法的基础.
3. execute() 方法, 是 exchange() 方法的基础.
4. getForEntity() 方法, 发送一个 GET 请求, 返回一个通用的 ResponseEntity 对象, 使用该对象可以得到 Response 字符串.
5. getForObject() 方法, 发送一个 GET 请求, 返回一个 pojo 对象.
6. headForHeaders() 方法, 发送一个 HEAD 请求, 返回包含特定资源 url 的 http 头.
8. optionsForAllow() 方法, 发送一个 HTTP OPTIONS 请求, 返回对于特定 url 的 Allow 头信息.
9. PostForEntity() 方法, 发送一个 Post 请求, 返回一个 ResponseEntity 对象, 这个对象是从响应体映射而来.
10. PostForObject() 方法, 发送一个 POST 请求, 返回一个特定的对象, 该对象是从响应体映射而来.
11. PostForLocation() 方法, 发送一个 POST 请求, 返回新创建资源的 URL.
12. put() 方法, 发送 PUT 请求.

--------------------------
获取 plain json
--------------------------

ResponseEntity<String> response=restTemplate.getForEntity(url, String.class)
// 获取包含 plain text Body 的 response
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
// 获取 status code
System.out.println("status code:" + response.getStatusCode());
// 使用 jackson 解析 json 字符串
// class: com.fasterxml.jackson.databind.ObjectMapper
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(response.getBody());
JsonNode value = root.path("type");

--------------------------
获取 Pojo 对象
--------------------------
如果 Rest 服务返回下面的 json 格式:
{ "firstName":"John" , "lastName":"Doe" }

RestTemplate 很容易可以将 json 转成对象:
Employee foo = restTemplate.getForObject(url, Employee.class);

如果 Rest 服务返回下面的 json 格式, json 中有一个根节点 employees, 其包含了多个 Employee 信息.

{
"employees": [
{ "firstName":"John" , "lastName":"Doe" },
{ "firstName":"Anna" , "lastName":"Smith" },
{ "firstName":"Peter" , "lastName":"Jones" }
]
}

对于这种格式的 json, 我们仍然可以使用 getForObject() 方法, 只要基于 Employee 类 做个 list wrapper 类即可.

public class EmployeeList {
private List<Employee> employees;
public EmployeeList() {
employees = new ArrayList<>();
}
// standard constructor and getter/setter
}
EmployeeList response = restTemplate.getForObject(
"http://localhost:8080/employees",
EmployeeList.class);
List<Employee> employees = response.getEmployees();

--------------------------
获取 json 数组对象
--------------------------
虽然 restTemplate.getForObject() 能很方便地将 json 转成 pojo, 但仅仅适合于处理单个对象的情形. 下面的 json 直接返回了一个数组, 这时使用 getForObject() 就不管用了.

[
{ "firstName":"John" , "lastName":"Doe" },
{ "firstName":"Anna" , "lastName":"Smith" },
{ "firstName":"Peter" , "lastName":"Jones" }
]

我们可以使用 exchange() 方法, 最关键一点是将 List<Employee> 类型传进去, 这样 RestTemplate 就知道如何将 json 数组转成 object list 了.

ResponseEntity<List<Employee>> response = restTemplate.exchange(
"http://localhost:8080/employees/",
HttpMethod.GET,
null,
new ParameterizedTypeReference<List<Employee>>(){});
List<Employee> employees = response.getBody();

--------------------------
向 url 传参
--------------------------
在 POST 和 GET 等方法, 最后一个形参往往是 url 参数变量, 比如:
getForEntity(String url,Class responseType,Object...urlVariables)
getForEntity(String url,Class responseType,Map urlVariables)

处理方式 1:
如果要使用数组或可变参数方式传入 url param, url 的参数必须使用数字下标来占位.

String url = http://USER-SERVICE/user.do?name={1}&age={2};
String[] urlVariables=["jason",26];

处理方式 2:
如果要 Map 传入 url param, url 的参数必须使用 named 方式占位

String url = http://USER-SERVICE/user.do?name={name}&age={age};
Map<String, Object> urlVariables = new HashMap<String, Object>();
urlVariables.put("name",jason);
urlVariables.put("age",26);

--------------------------
设置 header, Post 一个 json 串
--------------------------

String url="url";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
String body="some json body";
HttpEntity<String> requestEntity = new HttpEntity<String>(body, headers);
ResponseEntity<String> response= restTemplate.postForEntity(url, requestEntity, String.class);

HttpEntity 经常被用到, 它可以将 Headers 和要提交的数据合并成一个对象, 作为 request 对象传参给 POST/PUT/PATCH 等很多方法.

--------------------------
Post 一个对象 list
--------------------------
Post 操作可以直接使用 restTemplate.postForObject() 方法, 该方法即可 Post 单个对象, 也可以 Post 对象的 List.

List<Employee> newEmployees = new ArrayList<>();
newEmployees.add(new Employee(3, "Intern"));
newEmployees.add(new Employee(4, "CEO")); restTemplate.postForObject(
"http://localhost:8080/employees/",
newEmployees,
ResponseEntity.class);

--------------------------
使用 HEAD 获取 headers
--------------------------

HttpHeaders httpHeaders = restTemplate.headForHeaders(fooResourceUrl);
assertTrue(httpHeaders.getContentType().includes(MediaType.APPLICATION_JSON));

--------------------------
文件上传下载
--------------------------
参考 https://www.jianshu.com/p/bbd9848c0cfc

@Test
public void upload() throws Exception {
Resource resource = new FileSystemResource("/home/lake/github/wopi/build.gradle");
MultiValueMap multiValueMap = new LinkedMultiValueMap();
multiValueMap.add("username","lake");
multiValueMap.add("files",resource);
ActResult result = testRestTemplate.postForObject("/test/upload",multiValueMap,ActResult.class);
Assert.assertEquals(result.getCode(),0);
} @Test
public void download() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.set("token","xxxxxx");
HttpEntity formEntity = new HttpEntity(headers);
String[] urlVariables = new String[]{"admin"};
ResponseEntity<byte[]> response = testRestTemplate.exchange("/test/download?username={1}",HttpMethod.GET,formEntity,byte[].class,urlVariables);
if (response.getStatusCode() == HttpStatus.OK) {
Files.write(response.getBody(),new File("/home/lake/github/file/test.gradle"));
}
}

--------------------------
定制化 RestTemplate
--------------------------
增加一个自定义 ErrorHandler:
restTemplate.setErrorHandler(errorHandler);

设定 httpClient 的工厂类:
restTemplate.setRequestFactory(requestFactory);
可以为 RestTemplate 设定 httpClient 的工厂类, 主要有两个工厂类:
1. SimpleClientHttpRequestFactory 工厂类, 这是缺省的工厂类, 底层用的是 jdk 的 HttpConnection, 默认超时为-1.
2. HttpComponentsClientHttpRequestFactory 底层用的是 Apache HttpComponents HttpClient, 比 JDK 的 HttpConnection 强大, 可以配置连接池和证书等, 支持 https.
3. OkHttp3ClientHttpRequestFactory 底层使用的是 square 公司开源的 OkHttp, 该客户端支持 https 等高级特性,  pom.xml 需要增加依赖.
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
            <version>3.8.1</version>
        </dependency>

====================================
参考
====================================
https://www.jianshu.com/p/bbd9848c0cfc
http://www.cnblogs.com/okong/p/springcloud-four.html
https://my.oschina.net/lifany/blog/688889
http://www.cnblogs.com/okong/p/springcloud-four.html
https://blog.csdn.net/QiaoRui_/article/details/80453799

https://spring.io/guides/gs/consuming-rest/
https://www.tutorialspoint.com/spring_boot/spring_boot_rest_template.htm
https://www.baeldung.com/rest-template
https://www.baeldung.com/spring-rest-template-list

SpringBoot系列: RestTemplate 快速入门的更多相关文章

  1. SpringBoot系列: JdbcTemplate 快速入门

    对于一些小的项目, 我们没有必要使用MyBatis/JPA/Hibernate等重量级技术, 直接使用Spring JDBC 即可, Spring JDBC 是对 jdbc的简单封装, 很容易掌握. ...

  2. Flask开发系列之快速入门

    Flask开发系列之快速入门 文档 一个最小的应用 调试模式 路由 变量规则 构造 URL HTTP 方法 静态文件 模板渲染 访问请求数据 环境局部变量 请求对象 文件上传 Cookies 重定向和 ...

  3. Springboot 完整搭建快速入门,必看!

    前言 手把手教你Springboot微服务项目搭建快速入门,通过本文学习Springboot的搭建快速入门,掌握微服务大致的配置服务,后续将会继续将核心组件引入到项目中,欢迎关注,点赞,转发. Spr ...

  4. BIML 101 - ETL数据清洗 系列 - BIML 快速入门教程 - 序

    BIML 101 - BIML 快速入门教程 做大数据的项目,最花时间的就是数据清洗. 没有一个相对可靠的数据,数据分析就是无木之舟,无水之源. 如果你已经进了ETL这个坑,而且预算有限,并且有大量的 ...

  5. SpringBoot整合ActiveMQ快速入门

    Spring Boot 具有如下特性: 为基于 Spring 的开发提供更快的入门体验 开箱即用,没有代码生成,也无需 XML 配置.同时也可以修改默认值来满足特定的需求. 提供了一些大型项目中常见的 ...

  6. springboot笔记02——快速入门quickstart

    前言 学习一个新的框架,往往会用一个quickstart快速入门,这次就写一下springboot的quickstart程序. 开发环境 JDK 1.8 Springboot 2.1.6 Maven ...

  7. SpringBoot简介与快速入门

    一.SpringBoot简介 1.1 原有Spring优缺点分析 1.1.1 Spring的优点分析 Spring是Java企业版(Java Enterprise Edition,JEE,也称J2EE ...

  8. SpringBoot系列之从入门到精通系列教程

    对应SpringBoot系列博客专栏,例子代码,本博客不定时更新 Spring框架:作为JavaEE框架领域的一款重要的开源框架,在企业应用开发中有着很重要的作用,同时Spring框架及其子框架很多, ...

  9. BIML 101 - ETL数据清洗 系列 - BIML 快速入门教程 - 连接数据库执行SQL语句

    BIML 101 - BIML 快速入门教程 第一节 连接数据库执行SQL语句 本小节将用BIML建一个简单的可以执行的包. 新建一个biml文件,贴入下面的代码 1 <Biml xmlns=& ...

随机推荐

  1. 在java web项目中实现随项目启动的额外操作

    前言 在web项目中经常会遇到在项目启动初始,会要求做一些逻辑的实现,比如实现一个消息推送服务,实现不同类型数据同步的回调操作初始化,或则通知其他客户服务器本项目即将启动,等等.对于这种要求,目前个人 ...

  2. Java程序设计与数据结构导论--读后感

    与我前面所读的<Java7基础教程>相比,此书不适合自学,更适合作为教材使用. 虽然此书完整覆盖了Java的知识点和数据结构的基础问题,并且对每个部分都做了基本说明.但是因为没有深入展开, ...

  3. JS第一部分--ECMAScript5.0标准语法 (JS基础语法)

    一,调试语句 二,JS的引入方式 三,变量的使用 四,基本的数据类型 4.1,基本数据类型转换 4.2,字符串的常用方法 五,复杂数据类型 5.1,Array(数组)及常用方法 六,流程控制( 逻辑与 ...

  4. bsp makefile2

    1. grep "bsp_dir" -r ./  -s  --exclude-dir "*.git" 用这个加快目录定位-- 2.编译所有子目录 for dir ...

  5. ThreadPoolExecutor解析

    前言:在最新的阿里规范中强制使用ThreadPoolExecutor方式创建线程池,不允许使用Executors,因此有必要对ThreadPoolExecutor进行进一步了解. 1.ThreadPo ...

  6. Java多线程之ReentrantLock与Condition

    一.ReentrantLock 1.ReentrantLock简介 ReentrantLock是一个可重入的互斥锁,又被称为“独占锁”.ReentrantLock 类实现了 Lock ,它拥有与 sy ...

  7. Filebeat配置参考手册

    Filebeat的配置参考 指定要运行的模块 前提: 在运行Filebeat模块之前,需要安装并配置Elastic堆栈: 安装Ingest Node GeoIP和User Agent插件.这些插件需要 ...

  8. Java的get、post请求

    URLConnection package com.shuzf.http; import java.io.BufferedReader; import java.io.IOException; imp ...

  9. vs2015第二次装安装不能选择路径问题解决方法

    vs2015卸载后注册表还会存在vs2015的信息,下次安装的时候会读注册表里面记录的路径,不能自己选择路径. 解决方法: 1.在vs安装文件的路径打开命令,shift+鼠标右键 2.输入命令:cn_ ...

  10. P1577 切绳子

    P1577 切绳子 题目描述 有N条绳子,它们的长度分别为Li.如果从它们中切割出K条长度相同的 绳子,这K条绳子每条最长能有多长?答案保留到小数点后2位. 输入输出格式 输入格式: 第一行两个整数N ...