1. 背景
因为业务关系,要和许多不同第三方公司进行对接。这些服务商都提供基于 http 的 api。然而每家公司提供 api 具体细节差异很大。有的基于 RESTFUL
标准,有的基于传统的 http 标准;有的须要在 header
里搁置签名,有的须要 SSL
的双向认证,有的只须要 SSL
的单向认证;有的以 JSON
形式进行序列化,有的以XML
形式进行序列化。相似于这样细节的差异太多了。
不同的公司 API 标准不一样,这很失常。然而对于我来说,我如果想要代码变得优雅。我就必须解决一个痛点:
不同服务商 API 那么多的差别点,如何能力保护一套不波及业务的公共 http 调用套件。最好通过配置或者简略的参数就能辨别开来。进行不便的调用?
我当然晓得有很多优良的赫赫有名的 http 开源框架能够实现任何模式的 http 调用,在多年的开发教训中我都有应用过。比方 apache
的httpClient
包,十分优良的Okhttp
,jersey client
。
这些 http
开源框架的接口应用相对来说,都不太一样。不论选哪个,在我这个场景里来说,我都不心愿在调用每个第三方的 http api 时写上一堆 http 调用代码。
所以,在这个场景里,我得对每种不同的 http api 进行封装。这样的代码能力更加优雅,业务代码和 http 调用逻辑耦合度更低。
惋惜,我比拟懒。一来感觉封装起来比拟费时间,二来觉对封装这种底层 http 调用来说,应该有更好的抉择。不想本人再去造轮子。
于是,我发现了一款优良的开源 http 框架,能屏蔽不同细节 http api 所带来的所有差别。能通过简略的配置像调用 rpc 框架一样的去实现极为简单的 http 调用。
Forest
https://gitee.com/dt_flys/forest
2. 上手
Forest
反对了 Springboot
的主动拆卸,所以只须要引入一个依赖就行
<dependency>
<groupId>com.dtflys.forest</groupId>
<artifactId>spring-boot-starter-forest</artifactId>
<version>1.3.0</version>
</dependency>
定义本人的接口类
public interface MyClient {@Request(url = "http://baidu.com")
String simpleRequest();
@Request(
url = "http://ditu.amap.com/service/regeo",
dataType = "json"
)
Map getLocation(@DataParam("longitude") String longitude, @DataParam("latitude") String latitude);
}
在启动类里配置代理接口类的扫描包
@SpringBootApplication
@ForestScan(basePackages = "com.example.demo.forest")
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);
}
}
这时候,你就能够从 spring 容器中注入你的代理接口,像调用本地办法一样去调用 http 的 api 了
@Autowired
private MyClient myClient;
@Override
public void yourMethod throws Exception {Map result = myClient.getLocation("124.730329","31.463683");
System.out.println(JSON.toJSONString(result,true));
}
日志打印,Forest
打印了外部所用的 http 框架,和理论申请 url 和返回。当然日志能够通过配置去管制开关。
3. 特点
我感觉对于尤其是做对接第三方 api 的开发同学来说,这款开源框架能帮你进步很多效率。
Forest
底层封装了 2 种不同的 http 框架:Apache httpClient
和OKhttp
。所以这个开源框架并没有对底层实现进行反复造轮子,而是在易用性下面下足了功夫。
我用 Forest
最终实现了和多个服务商 api 对接的我的项目,这些格调迥异的 API,我仅用了 1 个小时工夫就把他们转化为了本地办法。而后我的项目顺利上线。
Forest
作为一款更加高层的 http 框架,其实你并不需要写很多代码,大多数时候,你仅通过一些配置就能实现 http 的本地化调用。而这个框架所能笼罩的面,却十分之广,满足你绝大多数的 http 调用申请。
Forest
有以下特点:
- 以
Httpclient
和OkHttp
为后端框架 - 通过调用本地办法的形式去发送 Http 申请, 实现了业务逻辑与 Http 协定之间的解耦
- 相比 Feign 更轻量,不依赖
Spring Cloud
和任何注册核心 - 反对所有申请办法:
GET
,HEAD
,OPTIONS
,TRACE
,POST
,DELETE
,PUT
,PATCH
- 反对灵便的模板表达式
- 反对过滤器来过滤传入的数据
- 基于注解、配置化的形式定义
Http
申请 - 反对
Spring
和Springboot
集成 - 实现
JSON
和XML
的序列化和反序列化 - 反对 JSON 转换框架:
Fastjson
,Jackson
,Gson
- 反对
JAXB
模式的XML
转换 - 反对
SSL
的单向和双向加密 - 反对 http 连接池的设定
- 能够通过
OnSuccess
和OnError
接口参数实现申请后果的回调 - 配置简略,个别只须要
@Request
一个注解就能实现绝大多数申请的定义 - 反对异步申请调用
4. 两个很棒的性能
这里不对应用形式和配置形式一一形容,有趣味的能够去浏览具体文档:
https://dt_flys.gitee.io/forest
这里只想剖析这个框架 2 个我认为比拟好的性能
4.1 模板表达式和参数的映射绑定性能
模板表达式在应用的时候特地不便,举个栗子
@Request(url = "${0}/send?un=${1}&pw=${2}&ph=${3}&ct=${4}",
type = "get",
dataType = "json"
)
public Map send(
String base,
String userName,
String password,
String phone,
String content
);
上述是用序号下标进行取值,也能够通过名字进行取值:
@Request(url = "${base}/send?un=${un}&pw=${pw}&ph=${3}&ct=${ct}",
type = "get",
dataType = "json"
)
public Map send(@DataVariable("base") String base,
@DataVariable("un") String userName,
@DataVariable("pw") String password,
@DataVariable("ph") String phone,
@DataVariable("ct") String content
);
甚至于能够这样简化写:
@Request(url = "${base}/send",
type = "get",
dataType = "json"
)
public Map send(@DataVariable("base") String base,
@DataParam("un") String userName,
@DataParam("pw") String password,
@DataParam("ph") String phone,
@DataParam("ct") String content
);
以上三种写法是等价的
当然你也能够把参数绑定到 header 和 body 里去,你甚至于能够用一些表达式简略的把对象序列化成 json 或者 xml:
@Request(url = "${base}/pay",
contentType = "application/json",
type = "post",
dataType = "json",
headers = {"Authorization: ${1}"},
data = "${json($0)}"
)
public PayResponse pay(PayRequest request, String auth);
当然数据绑定这块详情请参阅文档
4.2 对 HTTPS
的反对
以前用其余 http 框架解决 https 的时候,总感觉特地麻烦,尤其是双向证书。每次碰到问题也只能去 baidu。而后依据他人的教训来批改本人的代码。
Forest
对于这方面也想的很周到,底层完满封装了对 https 单双向证书的反对。也是只有通过简略的配置就能迅速实现。举个双向证书栗子:
@Request(url = "${base}/pay",
contentType = "application/json",
type = "post",
dataType = "json",
keyStore = "pay-keystore",
data = "${json($0)}"
)
public PayResponse pay(PayRequest request);
其中 pay-keystore
对应着 application.yml
里的ssl-key-stores
forest:
...
ssl-key-stores:
- id: pay-keystore
file: test.keystore
keystore-pass: 123456
cert-pass: 123456
protocols: SSLv3
这样设置,就 ok 了,剩下的,就是本地代码模式的调用了。
5. 最初
Forest
有很多其余的性能设定,如果感兴趣的同学还请认真去浏览文档和示例。
然而我想说的是,置信看到这里,很多人肯定会说,这不就是 Feign
吗?
我在开发 Spring Cloud
我的项目的时候,也用过一段时间 Feign
,个人感觉Forest
确实在配置和用法上和 Feign
的设计很像,但 Feign
的角色更多是作为 Spring Cloud
生态里的一个成员。充当 RPC 通信的角色,其承当的不仅是 http 通信,还要对注册核心下发的调用地址进行负载平衡。
而 Forest
这个开源我的项目其定位则是一个高阶的 http 工具,主打敌对和易用性。从应用角度登程,个人感觉 Forest
配置性更加简略间接。提供的很多性能也能解决很多人的痛点。
开源精力难能可贵,好的开源须要大家的添砖加瓦和反对。心愿这篇文章能给大家在抉择 http 客户端框架时带来一个新的抉择:Forest
6. 分割作者
微信关注「jishuyuanren」获取更多技术干货