一、简介
okhttp是一款由square公司开源的java版本http客户端工具。
square公司还开源了基于okhttp进一步封装的retrofit工具,用来支持通过接口的方式发起http请求。
retrofit-spring-boot-starter实现了retrofit与springboot框架快速整合,并且支持了部分功能增强,从而极大的简化spring-boot项目下http接口调用开发
二、springboot整合retrofit
1.导入依赖
<dependency>
<groupid>com.github.lianjiatech</groupid>
<artifactid>retrofit-spring-boot-starter</artifactid>
<version>2.2.22</version>
</dependency>
2.编写远程测试接口
package com.hl.springbootmybatis.controller;
import cn.hutool.core.collection.listutil;
import cn.hutool.json.jsonutil;
import lombok.data;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.multipartfile;
import java.util.list;
import java.util.map;
/**
* 测试远程被调用的retrofit接口
*/
@restcontroller
@requestmapping("/test/retrofit/remote")
public class remoteretrofitcontroller {
@getmapping("/t1")
public list<string> t1(@requestparam("name") string name) {
return listutil.of(name);
}
@deletemapping("/t2/{name}")
public list<string> t2(@pathvariable("name") string name) {
return listutil.of(name);
}
@postmapping("/t3")
public map<string, object> t3(@requestbody map<string, object> params) {
return params;
}
@putmapping("/t4")
public map<string, object> t4(@requestbody map<string, object> params) {
return params;
}
@postmapping("/t5")
public map<string, object> t5(@requestparam map<string, object> params) {
return params;
}
@postmapping("/t6")
public string t6(retrofittestmodel model) {
return jsonutil.tojsonstr(model);
}
@postmapping("/t7")
public string t7(string name) {
return name;
}
@postmapping("/t8")
public string t8(@requestheader("name") string name) {
return name;
}
@postmapping("/t9")
public string t9(@requestheader("name") string name, @requestheader("token") string token) {
return name + ";" + token;
}
@getmapping("/t10/{name}")
public list<string> t10(@pathvariable("name") string name) {
return listutil.of(name);
}
@data
public static class retrofittestmodel {
private string name;
}
}
3.编写本地测试接口
3.1 retrofit的配置信息
###############retrofit配置############### #连接池相关配置 retrofit.global-connect-timeout-ms= 5000 retrofit.pool.test.max-idle-connection=3 retrofit.pool.test.keep-alive-second=100 #日志打印拦截器配置,可以继承baselogginginterceptor实现自己的日志记录方式 retrofit.logging-interceptor=com.github.lianjiatech.retrofit.spring.boot.interceptor.defaultlogginginterceptor #异常格式化处理,可以继承basehttpexceptionmessageformatter,实现自己的异常格式化 retrofit。http-exception-message-formatter=com.github.lianjiatech.retrofit.spring.boot.interceptor.defaulthttpexceptionmessageformatter
3.2 本地和测试接口
新建接口,添加@retrofitclient注解,配置baseurl和poolname,poolname对应上一步的配置
package com.yolo.springbootretrofit.config;
import com.github.lianjiatech.retrofit.spring.boot.annotation.retrofitclient;
import retrofit2.http.*;
import java.util.list;
import java.util.map;
@retrofitclient(baseurl = "http://localhost:9093/",poolname = "test")
public interface httpclient {
@get("test/retrofit/remote/t1")
list<string> t1(@query("name") string name);
/**路径中传参 /{}*/
@delete("test/retrofit/remote/t2/{name}")
string t2(@path("name") string name);
/**使用请求体传送json*/
@post("test/retrofit/remote/t3")
map<string, object> t3(@body map<string, object> params);
/**put请求使用请求体传送json*/
@put("test/retrofit/remote/t4")
map<string, object> t4(@body map<string, object> params);
/**多个url参数使用map传递*/
@post("test/retrofit/remote/t5")
map<string, object> t5(@querymap map<string, object> params);
/**form表单传送数据,多个参数使用map传递*/
@formurlencoded
@post("test/retrofit/remote/t6")
map<string, object> t6(@fieldmap map<string, object> model);
/**form表单传送数据,多个参数一个一个传递*/
@formurlencoded
@post("test/retrofit/remote/t7")
string t7(@field("name") string name);
/**header传送数据,多个参数一个一个传递*/
@post("test/retrofit/remote/t8")
string t8(@header("name") string name);
/**header传送数据,多个参数一起传递*/
@post("test/retrofit/remote/t9")
@headers({"name:zhangsan", "token:lisi"})
string t9();
/**自定义全路径,避免重新写一个httpclient **/
@get()
list<string> t10(@url string url) ;
}
3.3 测试
package com.yolo.springbootretrofit.controller;
import com.yolo.springbootretrofit.config.httpclient;
import org.springframework.web.bind.annotation.getmapping;
import org.springframework.web.bind.annotation.requestmapping;
import org.springframework.web.bind.annotation.restcontroller;
import javax.annotation.resource;
import java.util.hashmap;
import java.util.list;
import java.util.map;
@restcontroller
@requestmapping("/test/retrofit")
public class retrofittestcontroller {
@resource
private httpclient httpclient;
@getmapping("/t1")
public list<string> t1() {
return httpclient.t1("zhangsan");
}
@getmapping("t2")
public string t2() {
return httpclient.t2("zhangsan");
}
@getmapping("t3")
public map<string, object> t3() {
map<string, object> map = new hashmap<>();
map.put("name", "zhangsan");
return httpclient.t3(map);
}
@getmapping("t4")
public map<string, object> t4() {
map<string, object> map = new hashmap<>();
map.put("name", "zhangsan");
return httpclient.t4(map);
}
@getmapping("t5")
public map<string, object> t5() {
map<string, object> map = new hashmap<>();
map.put("name", "zhangsan");
return httpclient.t5(map);
}
@getmapping("t6")
public map<string, object> t6() {
map<string, object> map = new hashmap<>();
map.put("name", "zhangsan");
return httpclient.t6(map);
}
@getmapping("t7")
public string t7() {
return httpclient.t7("zhangsan");
}
@getmapping("t8")
public string t8() {
return httpclient.t8("zhangsan");
}
@getmapping("t9")
public string t9() {
return httpclient.t9();
}
@getmapping("t10")
public list<string> t10() {
string s = "http://localhost:9093/test/retrofit/remote/t10/lisi";
return httpclient.t10(s);
}
}
4.编写拦截器
可是通过编写拦截器来拦截retrofit的请求,在请求中添加一些统一的处理,拦截器需要继承basepathmatchinterceptor并重写dointercept方法
接口上使用@intercept进行标注
package com.yolo.springbootretrofit.config;
import com.github.lianjiatech.retrofit.spring.boot.interceptor.basepathmatchinterceptor;
import okhttp3.headers;
import okhttp3.httpurl;
import okhttp3.request;
import okhttp3.response;
import org.springframework.stereotype.component;
import java.io.ioexception;
@component
public class simpleinterceptor extends basepathmatchinterceptor {
@override
protected response dointercept(chain chain) throws ioexception {
request request = chain.request();
httpurl url = request.url();
url = url.newbuilder().addqueryparameter("time", string.valueof(system.currenttimemillis()))
.build();
headers headers = request.headers();
headers = headers.newbuilder().add("add-header", "lalala").build();
request = request.newbuilder().headers(headers)
.url(url).build();
return chain.proceed(request);
}
}
在接口类中添加@interceptor注解,handler属性为上面定义的拦截器,include属性为拦截的url,exclude为不拦截的url
@intercept(handler = simpleinterceptor.class, include = {"/demo/test/**"}) //添加拦截器
5.自定义注解拦截器
注意必须添加@interceptmark
package com.yolo.springbootretrofit.config;
import com.github.lianjiatech.retrofit.spring.boot.annotation.interceptmark;
import com.github.lianjiatech.retrofit.spring.boot.interceptor.basepathmatchinterceptor;
import java.lang.annotation.*;
@retention(retentionpolicy.runtime)
@target(elementtype.type)
@documented
@interceptmark //必须加这个注解
public @interface sign {
/**
* 密钥key
* 支持占位符形式配置。
*
* @return
*/
string accesskeyid();
/**
* 密钥
* 支持占位符形式配置。
*
* @return
*/
string accesskeysecret();
/**
* 拦截器匹配路径
*
* @return
*/
string[] include() default {"/**"};
/**
* 拦截器排除匹配,排除指定路径拦截
*
* @return
*/
string[] exclude() default {};
/**
* 处理该注解的拦截器类
* 优先从spring容器获取对应的bean,如果获取不到,则使用反射创建一个!
*
* @return
*/
class<? extends basepathmatchinterceptor> handler() default signinterceptor.class;
}
accesskeyid和accesskeysecret字段值会依据@sign注解的accesskeyid()和accesskeysecret()值自动注入,如果@sign指定的是占位符形式的字符串,则会取配置属性值进行注入。
另外,accesskeyid和accesskeysecret字段必须提供setter方法
package com.yolo.springbootretrofit.config;
import com.github.lianjiatech.retrofit.spring.boot.interceptor.basepathmatchinterceptor;
import okhttp3.request;
import okhttp3.response;
import org.springframework.stereotype.component;
import java.io.ioexception;
@component
public class signinterceptor extends basepathmatchinterceptor {
private string accesskeyid;
private string accesskeysecret;
public void setaccesskeyid(string accesskeyid) {
this.accesskeyid = accesskeyid;
}
public void setaccesskeysecret(string accesskeysecret) {
this.accesskeysecret = accesskeysecret;
}
@override
public response dointercept(chain chain) throws ioexception {
request request = chain.request();
request newreq = request.newbuilder()
.addheader("accesskeyid", accesskeyid)
.addheader("accesskeysecret", accesskeysecret)
.build();
return chain.proceed(newreq);
}
}
接口上使用@sign
@retrofitclient(baseurl = "${test.baseurl}")
@sign(accesskeyid = "${test.accesskeyid}", accesskeysecret = "${test.accesskeysecret}", exclude = {"/api/test/person"})
public interface httpapi {
@get("person")
result<person> getperson(@query("id") long id);
@post("saveperson")
result<person> saveperson(@body person person);
}
三、连接池管理
默认情况下,所有通过retrofit发送的http请求都会使用max-idle-connections=5 keep-alive-second=300的默认连接池。当然,我们也可以在配置文件中配置多个自定义的连接池,然后通过@retrofitclient的poolname属性来指定使用。比如我们要让某个接口下的请求全部使用poolname=test1的连接池
1.配置连接池
retrofit:
# 连接池配置
pool:
test1:
max-idle-connections: 3
keep-alive-second: 100
test2:
max-idle-connections: 5
keep-alive-second: 50
2.通过@retrofitclient的poolname属性来指定使用的连接池
@retrofitclient(baseurl = "${test.baseurl}", poolname="test1")
public interface httpapi {
@get("person")
result<person> getperson(@query("id") long id);
}
四、日志打印
很多情况下,我们希望将http请求日志记录下来。通过@retrofitclient的loglevel和logstrategy属性,您可以指定每个接口的日志打印级别以及日志打印策略。
retrofit-spring-boot-starter支持了5种日志打印级别(error, warn, info, debug, trace),默认info;支持了4种日志打印策略(none, basic, headers, body),默认basic。
4种日志打印策略含义如下:
none:no logs.basic:logs request and response lines.headers:logs request and response lines and their respective headers.body:logs request and response lines and their respective headers and bodies (if present)
retrofit-spring-boot-starter默认使用了defaultlogginginterceptor执行真正的日志打印功能,其底层就是okhttp原生的httplogginginterceptor。
当然,你也可以自定义实现自己的日志打印拦截器,只需要继承baselogginginterceptor(具体可以参考defaultlogginginterceptor的实现),然后在配置文件中进行相关配置即可。
retrofit: # 日志打印拦截器 logging-interceptor: com.github.lianjiatech.retrofit.spring.boot.interceptor.defaultlogginginterceptor
五、http异常信息格式化器
当出现http请求异常时,原始的异常信息可能阅读起来并不友好,因此retrofit-spring-boot-starter提供了http异常信息格式化器,用来美化输出http请求参数,默认使用defaulthttpexceptionmessageformatter进行请求数据格式化。
你也可以进行自定义,只需要继承basehttpexceptionmessageformatter,再进行相关配置即可
retrofit: # http异常信息格式化器 http-exception-message-formatter: com.github.lianjiatech.retrofit.spring.boot.interceptor.defaulthttpexceptionmessageformatter
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论