springboot整合retrofit實現(xiàn)本地接口調(diào)用遠程服務(wù)方式
一、簡介
okhttp是一款由square公司開源的java版本http客戶端工具。
square公司還開源了基于okhttp進一步封裝的retrofit工具,用來支持通過接口的方式發(fā)起http請求。
retrofit-spring-boot-starter實現(xiàn)了Retrofit與SpringBoot框架快速整合,并且支持了部分功能增強,從而極大的簡化spring-boot項目下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.編寫遠程測試接口
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; /** * 測試遠程被調(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.編寫本地測試接口
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實現(xiàn)自己的日志記錄方式 retrofit.logging-interceptor=com.github.lianjiatech.retrofit.spring.boot.interceptor.DefaultLoggingInterceptor #異常格式化處理,可以繼承BaseHttpExceptionMessageFormatter,實現(xiàn)自己的異常格式化 retrofit。http-exception-message-formatter=com.github.lianjiatech.retrofit.spring.boot.interceptor.DefaultHttpExceptionMessageFormatter
3.2 本地和測試接口
新建接口,添加@RetrofitClient
注解,配置baseUrl和poolName
,poolName
對應(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); /**使用請求體傳送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參數(shù)使用map傳遞*/ @POST("test/retrofit/remote/t5") Map<String, Object> t5(@QueryMap Map<String, Object> params); /**form表單傳送數(shù)據(jù),多個參數(shù)使用map傳遞*/ @FormUrlEncoded @POST("test/retrofit/remote/t6") Map<String, Object> t6(@FieldMap Map<String, Object> model); /**form表單傳送數(shù)據(jù),多個參數(shù)一個一個傳遞*/ @FormUrlEncoded @POST("test/retrofit/remote/t7") String t7(@Field("name") String name); /**header傳送數(shù)據(jù),多個參數(shù)一個一個傳遞*/ @POST("test/retrofit/remote/t8") String t8(@Header("name") String name); /**header傳送數(shù)據(jù),多個參數(shù)一起傳遞*/ @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的請求,在請求中添加一些統(tǒng)一的處理,攔截器需要繼承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 {}; /** * 處理該注解的攔截器類 * 優(yōu)先從spring容器獲取對應(yīng)的Bean,如果獲取不到,則使用反射創(chuàng)建一個! * * @return */ Class<? extends BasePathMatchInterceptor> handler() default SignInterceptor.class; }
accessKeyId
和accessKeySecret
字段值會依據(jù)@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
發(fā)送的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
執(zhí)行真正的日志打印功能,其底層就是okhttp
原生的HttpLoggingInterceptor
。
當然,你也可以自定義實現(xiàn)自己的日志打印攔截器,只需要繼承BaseLoggingInterceptor
(具體可以參考DefaultLoggingInterceptor
的實現(xiàn)),然后在配置文件中進行相關(guān)配置即可。
retrofit: # 日志打印攔截器 logging-interceptor: com.github.lianjiatech.retrofit.spring.boot.interceptor.DefaultLoggingInterceptor
五、HTTP異常信息格式化器
當出現(xiàn)http請求異常時,原始的異常信息可能閱讀起來并不友好,因此retrofit-spring-boot-starter
提供了HTTP異常信息格式化器,用來美化輸出http請求參數(shù),默認使用DefaultHttpExceptionMessageFormatter
進行請求數(shù)據(jù)格式化。
你也可以進行自定義,只需要繼承BaseHttpExceptionMessageFormatter
,再進行相關(guān)配置即可
retrofit: # Http異常信息格式化器 http-exception-message-formatter: com.github.lianjiatech.retrofit.spring.boot.interceptor.DefaultHttpExceptionMessageFormatter
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
關(guān)于SpringSecurity認證邏輯源碼分析
這篇文章主要介紹了關(guān)于SpringSecurity認證邏輯源碼分析,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-07-07關(guān)于Scanner中nextInt()、nextLine()等方法總結(jié)與問題解決
這篇文章主要介紹了關(guān)于Scanner中nextInt()、nextLine()等方法總結(jié)與問題解決,具有很好的參考價值,希望對大家有所幫助。2022-11-11IDEA中l(wèi)og4j 無法輸出到本地 properties配置無效問題
這篇文章主要介紹了IDEA中l(wèi)og4j 無法輸出到本地 properties配置無效問題,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下2019-10-10Java?常量池詳解之class文件常量池?和class運行時常量池
這篇文章主要介紹了Java?常量池詳解之class文件常量池?和class運行時常量池,常量池主要存放兩大類常量:字面量,符號引用,本文結(jié)合示例代碼對java class常量池相關(guān)知識介紹的非常詳細,需要的朋友可以參考下2022-12-12Maven的配置文件pom.xml詳解(含常用plugin)
pom.xml是Maven項目的核心配置文件,它是 項目對象模型 - Project Object Model(POM)的縮寫,本文我們將全面解析pom.xml,了解其結(jié)構(gòu)和屬性,以及如何使用它來管理項目,感興趣的朋友跟隨小編一起看看吧2024-08-08