一、简介
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
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论