springboot整合retrofit實(shí)現(xiàn)本地接口調(diào)用遠(yuǎn)程服務(wù)方式
一、簡(jiǎn)介
okhttp是一款由square公司開源的java版本http客戶端工具。
square公司還開源了基于okhttp進(jìn)一步封裝的retrofit工具,用來支持通過接口的方式發(fā)起http請(qǐng)求。
retrofit-spring-boot-starter實(shí)現(xiàn)了Retrofit與SpringBoot框架快速整合,并且支持了部分功能增強(qiáng),從而極大的簡(jiǎn)化spring-boot項(xiàng)目下http接口調(diào)用開發(fā)
二、springboot整合retrofit
1.導(dǎo)入依賴
<dependency> <groupId>com.github.lianjiatech</groupId> <artifactId>retrofit-spring-boot-starter</artifactId> <version>2.2.22</version> </dependency>
2.編寫遠(yuǎn)程測(cè)試接口
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; /** * 測(cè)試遠(yuǎn)程被調(diào)用的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.編寫本地測(cè)試接口
3.1 retrofit的配置信息
###############retrofit配置############### #連接池相關(guān)配置 retrofit.global-connect-timeout-ms= 5000 retrofit.pool.test.max-idle-connection=3 retrofit.pool.test.keep-alive-second=100 #日志打印攔截器配置,可以繼承BaseLoggingInterceptor實(shí)現(xiàn)自己的日志記錄方式 retrofit.logging-interceptor=com.github.lianjiatech.retrofit.spring.boot.interceptor.DefaultLoggingInterceptor #異常格式化處理,可以繼承BaseHttpExceptionMessageFormatter,實(shí)現(xiàn)自己的異常格式化 retrofit。http-exception-message-formatter=com.github.lianjiatech.retrofit.spring.boot.interceptor.DefaultHttpExceptionMessageFormatter
3.2 本地和測(cè)試接口
新建接口,添加@RetrofitClient
注解,配置baseUrl和poolName
,poolName
對(duì)應(yīng)上一步的配置
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); /**使用請(qǐng)求體傳送json*/ @POST("test/retrofit/remote/t3") Map<String, Object> t3(@Body Map<String, Object> params); /**put請(qǐng)求使用請(qǐng)求體傳送json*/ @PUT("test/retrofit/remote/t4") Map<String, Object> t4(@Body Map<String, Object> params); /**多個(gè)url參數(shù)使用map傳遞*/ @POST("test/retrofit/remote/t5") Map<String, Object> t5(@QueryMap Map<String, Object> params); /**form表單傳送數(shù)據(jù),多個(gè)參數(shù)使用map傳遞*/ @FormUrlEncoded @POST("test/retrofit/remote/t6") Map<String, Object> t6(@FieldMap Map<String, Object> model); /**form表單傳送數(shù)據(jù),多個(gè)參數(shù)一個(gè)一個(gè)傳遞*/ @FormUrlEncoded @POST("test/retrofit/remote/t7") String t7(@Field("name") String name); /**header傳送數(shù)據(jù),多個(gè)參數(shù)一個(gè)一個(gè)傳遞*/ @POST("test/retrofit/remote/t8") String t8(@Header("name") String name); /**header傳送數(shù)據(jù),多個(gè)參數(shù)一起傳遞*/ @POST("test/retrofit/remote/t9") @Headers({"name:zhangsan", "token:lisi"}) String t9(); /**自定義全路徑,避免重新寫一個(gè)HttpClient **/ @GET() List<String> t10(@Url String url) ; }
3.3 測(cè)試
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的請(qǐng)求,在請(qǐng)求中添加一些統(tǒng)一的處理,攔截器需要繼承BasePathMatchInterceptor
并重寫doIntercept
方法
接口上使用@Intercept
進(jìn)行標(biāo)注
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 //必須加這個(gè)注解 public @interface Sign { /** * 密鑰key * 支持占位符形式配置。 * * @return */ String accessKeyId(); /** * 密鑰 * 支持占位符形式配置。 * * @return */ String accessKeySecret(); /** * 攔截器匹配路徑 * * @return */ String[] include() default {"/**"}; /** * 攔截器排除匹配,排除指定路徑攔截 * * @return */ String[] exclude() default {}; /** * 處理該注解的攔截器類 * 優(yōu)先從spring容器獲取對(duì)應(yīng)的Bean,如果獲取不到,則使用反射創(chuàng)建一個(gè)! * * @return */ Class<? extends BasePathMatchInterceptor> handler() default SignInterceptor.class; }
accessKeyId
和accessKeySecret
字段值會(huì)依據(jù)@Sign
注解的accessKeyId()
和accessKeySecret()
值自動(dòng)注入,如果@Sign
指定的是占位符形式的字符串,則會(huì)取配置屬性值進(jìn)行注入。
另外,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); }
三、連接池管理
默認(rèn)情況下,所有通過Retrofit
發(fā)送的http請(qǐng)求都會(huì)使用max-idle-connections=5 keep-alive-second=300
的默認(rèn)連接池。當(dāng)然,我們也可以在配置文件中配置多個(gè)自定義的連接池,然后通過@RetrofitClient
的poolName
屬性來指定使用。比如我們要讓某個(gè)接口下的請(qǐng)求全部使用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請(qǐng)求日志記錄下來。通過@RetrofitClient
的logLevel
和logStrategy
屬性,您可以指定每個(gè)接口的日志打印級(jí)別以及日志打印策略。
retrofit-spring-boot-starter
支持了5種日志打印級(jí)別(ERROR
, WARN
, INFO
, DEBUG
, TRACE
),默認(rèn)INFO
;支持了4種日志打印策略(NONE
, BASIC
, HEADERS
, BODY
),默認(rèn)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
默認(rèn)使用了DefaultLoggingInterceptor
執(zhí)行真正的日志打印功能,其底層就是okhttp
原生的HttpLoggingInterceptor
。
當(dāng)然,你也可以自定義實(shí)現(xiàn)自己的日志打印攔截器,只需要繼承BaseLoggingInterceptor
(具體可以參考DefaultLoggingInterceptor
的實(shí)現(xiàn)),然后在配置文件中進(jìn)行相關(guān)配置即可。
retrofit: # 日志打印攔截器 logging-interceptor: com.github.lianjiatech.retrofit.spring.boot.interceptor.DefaultLoggingInterceptor
五、HTTP異常信息格式化器
當(dāng)出現(xiàn)http請(qǐng)求異常時(shí),原始的異常信息可能閱讀起來并不友好,因此retrofit-spring-boot-starter
提供了HTTP異常信息格式化器,用來美化輸出http請(qǐng)求參數(shù),默認(rèn)使用DefaultHttpExceptionMessageFormatter
進(jìn)行請(qǐng)求數(shù)據(jù)格式化。
你也可以進(jìn)行自定義,只需要繼承BaseHttpExceptionMessageFormatter
,再進(jìn)行相關(guān)配置即可
retrofit: # Http異常信息格式化器 http-exception-message-formatter: com.github.lianjiatech.retrofit.spring.boot.interceptor.DefaultHttpExceptionMessageFormatter
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
關(guān)于SpringSecurity認(rèn)證邏輯源碼分析
這篇文章主要介紹了關(guān)于SpringSecurity認(rèn)證邏輯源碼分析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07關(guān)于Scanner中nextInt()、nextLine()等方法總結(jié)與問題解決
這篇文章主要介紹了關(guān)于Scanner中nextInt()、nextLine()等方法總結(jié)與問題解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。2022-11-11IDEA中l(wèi)og4j 無法輸出到本地 properties配置無效問題
這篇文章主要介紹了IDEA中l(wèi)og4j 無法輸出到本地 properties配置無效問題,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-10-10Java?常量池詳解之class文件常量池?和class運(yùn)行時(shí)常量池
這篇文章主要介紹了Java?常量池詳解之class文件常量池?和class運(yùn)行時(shí)常量池,常量池主要存放兩大類常量:字面量,符號(hào)引用,本文結(jié)合示例代碼對(duì)java class常量池相關(guān)知識(shí)介紹的非常詳細(xì),需要的朋友可以參考下2022-12-12Maven的配置文件pom.xml詳解(含常用plugin)
pom.xml是Maven項(xiàng)目的核心配置文件,它是 項(xiàng)目對(duì)象模型 - Project Object Model(POM)的縮寫,本文我們將全面解析pom.xml,了解其結(jié)構(gòu)和屬性,以及如何使用它來管理項(xiàng)目,感興趣的朋友跟隨小編一起看看吧2024-08-08