<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          還在用HttpUtil?試試這款優(yōu)雅的HTTP客戶端工具吧,跟SpringBoot絕配!

          共 8168字,需瀏覽 17分鐘

           ·

          2022-05-16 16:07

          我們平時(shí)開發(fā)項(xiàng)目時(shí),就算是單體應(yīng)用,也免不了要調(diào)用一下其他服務(wù)提供的接口。此時(shí)就會(huì)用到HTTP客戶端工具,之前一直使用的是Hutool中的HttpUtil,雖然容易上手,但用起來頗為麻煩!最近發(fā)現(xiàn)一款更好用的HTTP客戶端工具Retrofit,你只需聲明接口就可發(fā)起HTTP請(qǐng)求,無需進(jìn)行連接、結(jié)果解析之類的重復(fù)操作,用起來夠優(yōu)雅,推薦給大家!

          簡(jiǎn)介

          Retrofit是適用于AndroidJava且類型安全的HTTP客戶端工具,在Github上已經(jīng)有39k+Star。其最大的特性的是支持通過接口的方式發(fā)起HTTP請(qǐng)求,類似于我們用Feign調(diào)用微服務(wù)接口的那種方式。

          SpringBoot是使用最廣泛的Java開發(fā)框架,但是Retrofit官方并沒有提供專門的Starter。于是有位老哥就開發(fā)了retrofit-spring-boot-starter,它實(shí)現(xiàn)了Retrofit與SpringBoot框架的快速整合,并且支持了諸多功能增強(qiáng),極大簡(jiǎn)化開發(fā)。今天我們將使用這個(gè)第三方Starter來操作Retrofit。

          使用

          在SpringBoot中使用Retrofit是非常簡(jiǎn)單的,下面我們就來體驗(yàn)下。

          依賴集成

          有了第三方Starter的支持,集成Retrofit僅需一步,添加如下依賴即可。


          <dependency>
          ????<groupId>com.github.lianjiatechgroupId>
          ????<artifactId>retrofit-spring-boot-starterartifactId>
          ????<version>2.2.18version>
          dependency>

          基本使用

          下面以調(diào)用mall-tiny-swagger中的接口為例,我們來體驗(yàn)下Retrofit的基本使用。

          • 首先我們準(zhǔn)備一個(gè)服務(wù)來方便遠(yuǎn)程調(diào)用,使用的是之前的mall-tiny-swagger這個(gè)Demo,打開Swagger看下,里面有一個(gè)登錄接口和需要登錄認(rèn)證的商品品牌CRUD接口,項(xiàng)目地址:https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-swagger
          • 我們先來調(diào)用下登錄接口試試,在application.yml中配置好mall-tiny-swagger的服務(wù)地址;
          remote:
          ??baseUrl:?http://localhost:8088/
          • 再通過@RetrofitClient聲明一個(gè)Retrofit客戶端,由于登錄接口是通過POST表單形式調(diào)用的,這里使用到了@POST@FormUrlEncoded注解;
          /**
          ?*?定義Http接口,用于調(diào)用遠(yuǎn)程的UmsAdmin服務(wù)
          ?*?Created?by?macro?on?2022/1/19.
          ?*/

          @RetrofitClient(baseUrl?=?"${remote.baseUrl}")
          public?interface?UmsAdminApi?{

          ????@FormUrlEncoded
          ????@POST("admin/login")
          ????CommonResult?login(@Field("username")?String?username,?@Field("password")?String?password);
          }
          • 如果你不太明白這些注解是干嘛的,看下下面的表基本就懂了,更具體的話可以參考Retrofit官方文檔;
          • 接下來在Controller中注入UmsAdminApi,然后進(jìn)行調(diào)用即可;
          /**
          ?*?Retrofit測(cè)試接口
          ?*?Created?by?macro?on?2022/1/19.
          ?*/

          @Api(tags?=?"RetrofitController",?description?=?"Retrofit測(cè)試接口")
          @RestController
          @RequestMapping("/retrofit")
          public?class?RetrofitController?{

          ????@Autowired
          ????private?UmsAdminApi?umsAdminApi;
          ????@Autowired
          ????private?TokenHolder?tokenHolder;

          ????@ApiOperation(value?=?"調(diào)用遠(yuǎn)程登錄接口獲取token")
          ????@PostMapping(value?=?"/admin/login")
          ????public?CommonResult?login(@RequestParam?String?username,?@RequestParam?String?password)?{
          ????????CommonResult?result?=?umsAdminApi.login(username,?password);
          ????????LoginInfo?loginInfo?=?result.getData();
          ????????if?(result.getData()?!=?null)?{
          ????????????tokenHolder.putToken(loginInfo.getTokenHead()?+?"?"?+?loginInfo.getToken());
          ????????}
          ????????return?result;
          ????}
          }
          • 為方便后續(xù)調(diào)用需要登錄認(rèn)證的接口,我創(chuàng)建了TokenHolder這個(gè)類,把token存儲(chǔ)到了Session中;
          /**
          ?*?登錄token存儲(chǔ)(在Session中)
          ?*?Created?by?macro?on?2022/1/19.
          ?*/

          @Component
          public?class?TokenHolder?{
          ????/**
          ?????*?添加token
          ?????*/

          ????public?void?putToken(String?token)?{
          ????????RequestAttributes?ra?=?RequestContextHolder.getRequestAttributes();
          ????????HttpServletRequest?request?=?((ServletRequestAttributes)?ra).getRequest();
          ????????request.getSession().setAttribute("token",?token);
          ????}

          ????/**
          ?????*?獲取token
          ?????*/

          ????public?String?getToken()?{
          ????????RequestAttributes?ra?=?RequestContextHolder.getRequestAttributes();
          ????????HttpServletRequest?request?=?((ServletRequestAttributes)?ra).getRequest();
          ????????Object?token?=?request.getSession().getAttribute("token");
          ????????if(token!=null){
          ????????????return?(String)?token;
          ????????}
          ????????return?null;
          ????}

          }
          • 接下來通過Swagger進(jìn)行測(cè)試,調(diào)用接口就可以獲取到遠(yuǎn)程服務(wù)返回的token了,訪問地址:http://localhost:8086/swagger-ui/

          注解式攔截器

          商品品牌管理接口,需要添加登錄認(rèn)證頭才可以正常訪問,我們可以使用Retrofit中的注解式攔截器來實(shí)現(xiàn)。

          • 首先創(chuàng)建一個(gè)注解式攔截器TokenInterceptor繼承BasePathMatchInterceptor,然后在doIntercept方法中給請(qǐng)求添加Authorization頭;
          /**
          ?*?給請(qǐng)求添加登錄Token頭的攔截器
          ?*?Created?by?macro?on?2022/1/19.
          ?*/

          @Component
          public?class?TokenInterceptor?extends?BasePathMatchInterceptor?{
          ????@Autowired
          ????private?TokenHolder?tokenHolder;

          ????@Override
          ????protected?Response?doIntercept(Chain?chain)?throws?IOException?{
          ????????Request?request?=?chain.request();
          ????????if?(tokenHolder.getToken()?!=?null)?{
          ????????????request?=?request.newBuilder()
          ????????????????????.header("Authorization",?tokenHolder.getToken())
          ????????????????????.build();
          ????????}
          ????????return?chain.proceed(request);
          ????}
          }
          • 創(chuàng)建調(diào)用品牌管理接口的客戶端PmsBrandApi,使用@Intercept注解配置攔截器和攔截路徑;
          /**
          ?*?定義Http接口,用于調(diào)用遠(yuǎn)程的PmsBrand服務(wù)
          ?*?Created?by?macro?on?2022/1/19.
          ?*/

          @RetrofitClient(baseUrl?=?"${remote.baseUrl}")
          @Intercept(handler?=?TokenInterceptor.class,?include?=?"/brand/**")
          public?interface?PmsBrandApi?{
          ????@GET("brand/list")
          ????CommonResult>?list(@Query("pageNum")?Integer?pageNum,?@Query("pageSize")?Integer?pageSize);

          ????@GET("brand/{id}")
          ????CommonResult?detail(@Path("id")?Long?id);

          ????@POST("brand/create")
          ????CommonResult?create(@Body?PmsBrand?pmsBrand);

          ????@POST("brand/update/{id}")
          ????CommonResult?update(@Path("id")?Long?id,?@Body?PmsBrand?pmsBrand);

          ????@GET("brand/delete/{id}")
          ????CommonResult?delete(@Path("id")?Long?id);
          }
          • 再在Controller中注入PmsBrandApi實(shí)例,并添加方法調(diào)用遠(yuǎn)程服務(wù)即可;
          /**
          ?*?Retrofit測(cè)試接口
          ?*?Created?by?macro?on?2022/1/19.
          ?*/

          @Api(tags?=?"RetrofitController",?description?=?"Retrofit測(cè)試接口")
          @RestController
          @RequestMapping("/retrofit")
          public?class?RetrofitController?{
          ????
          ????@Autowired
          ????private?PmsBrandApi?pmsBrandApi;

          ????@ApiOperation("調(diào)用遠(yuǎn)程接口分頁(yè)查詢品牌列表")
          ????@GetMapping(value?=?"/brand/list")
          ????public?CommonResult>?listBrand(@RequestParam(value?=?"pageNum",?defaultValue?=?"1")
          ????????????????????????????????????????????????????????@ApiParam("頁(yè)碼")?Integer?pageNum,
          ????????????????????????????????????????????????????????@RequestParam(value?=?"pageSize",?defaultValue?=?"3")
          ????????????????????????????????????????????????????????@ApiParam("每頁(yè)數(shù)量")?Integer?pageSize)?{
          ????????return?pmsBrandApi.list(pageNum,?pageSize);
          ????}

          ????@ApiOperation("調(diào)用遠(yuǎn)程接口獲取指定id的品牌詳情")
          ????@GetMapping(value?=?"/brand/{id}")
          ????public?CommonResult?brand(@PathVariable("id")?Long?id)?{
          ????????return?pmsBrandApi.detail(id);
          ????}

          ????@ApiOperation("調(diào)用遠(yuǎn)程接口添加品牌")
          ????@PostMapping(value?=?"/brand/create")
          ????public?CommonResult?createBrand(@RequestBody?PmsBrand?pmsBrand)?{
          ????????return?pmsBrandApi.create(pmsBrand);
          ????}
          ????@ApiOperation("調(diào)用遠(yuǎn)程接口更新指定id品牌信息")
          ????@PostMapping(value?=?"/brand/update/{id}")
          ????public?CommonResult?updateBrand(@PathVariable("id")?Long?id,?@RequestBody?PmsBrand?pmsBrand)?{
          ????????return?pmsBrandApi.update(id,pmsBrand);
          ????}

          ????@ApiOperation("調(diào)用遠(yuǎn)程接口刪除指定id的品牌")
          ????@GetMapping(value?=?"/delete/{id}")
          ????public?CommonResult?deleteBrand(@PathVariable("id")?Long?id)?{
          ????????return??pmsBrandApi.delete(id);
          ????}
          }
          • 在Swagger中調(diào)用接口進(jìn)行測(cè)試,發(fā)現(xiàn)已經(jīng)可以成功調(diào)用。

          全局?jǐn)r截器

          如果你想給所有請(qǐng)求都加個(gè)請(qǐng)求頭的話,可以使用全局?jǐn)r截器。

          創(chuàng)建SourceInterceptor類繼承BaseGlobalInterceptor接口,然后在Header中添加source請(qǐng)求頭。

          /**
          ?*?全局?jǐn)r截器,給請(qǐng)求添加source頭
          ?*?Created?by?macro?on?2022/1/19.
          ?*/

          @Component
          public?class?SourceInterceptor?extends?BaseGlobalInterceptor?{
          ????@Override
          ????protected?Response?doIntercept(Chain?chain)?throws?IOException?{
          ????????Request?request?=?chain.request();
          ????????Request?newReq?=?request.newBuilder()
          ????????????????.addHeader("source",?"retrofit")
          ????????????????.build();
          ????????return?chain.proceed(newReq);
          ????}
          }

          配置

          Retrofit的配置很多,下面我們講講日志打印、全局超時(shí)時(shí)間和全局請(qǐng)求重試這三種最常用的配置。

          日志打印

          • 默認(rèn)配置下Retrofit使用basic日志策略,打印的日志非常簡(jiǎn)單;
          • 我們可以將application.yml中的retrofit.global-log-strategy屬性修改為body來打印最全日志;
          retrofit:
          ??#?日志打印配置
          ??log:
          ????#?啟用日志打印
          ????enable:?true
          ????#?日志打印攔截器
          ????logging-interceptor:?com.github.lianjiatech.retrofit.spring.boot.interceptor.DefaultLoggingInterceptor
          ????#?全局日志打印級(jí)別
          ????global-log-level:?info
          ????#?全局日志打印策略
          ????global-log-strategy:?body
          • 修改日志打印策略后,日志信息更全面了;
          • Retrofit支持四種日志打印策略;
            • NONE:不打印日志;
            • BASIC:只打印日志請(qǐng)求記錄;
            • HEADERS:打印日志請(qǐng)求記錄、請(qǐng)求和響應(yīng)頭信息;
            • BODY:打印日志請(qǐng)求記錄、請(qǐng)求和響應(yīng)頭信息、請(qǐng)求和響應(yīng)體信息。

          全局超時(shí)時(shí)間

          有時(shí)候我們需要修改一下Retrofit的請(qǐng)求超時(shí)時(shí)間,可以通過如下配置實(shí)現(xiàn)。

          retrofit:
          ??#?全局連接超時(shí)時(shí)間
          ??global-connect-timeout-ms:?3000
          ??#?全局讀取超時(shí)時(shí)間
          ??global-read-timeout-ms:?3000
          ??#?全局寫入超時(shí)時(shí)間
          ??global-write-timeout-ms:?35000
          ??#?全局完整調(diào)用超時(shí)時(shí)間
          ??global-call-timeout-ms:?0

          全局請(qǐng)求重試

          • retrofit-spring-boot-starter支持請(qǐng)求重試,可以通過如下配置實(shí)現(xiàn)。
          retrofit:
          ??#?重試配置
          ??retry:
          ????#?是否啟用全局重試
          ????enable-global-retry:?true
          ????#?全局重試間隔時(shí)間
          ????global-interval-ms:?100
          ????#?全局最大重試次數(shù)
          ????global-max-retries:?2
          ????#?全局重試規(guī)則
          ????global-retry-rules:
          ??????-?response_status_not_2xx
          ??????-?occur_exception
          ????#?重試攔截器
          ????retry-interceptor:?com.github.lianjiatech.retrofit.spring.boot.retry.DefaultRetryInterceptor
          • 重試規(guī)則global-retry-rules支持如下三種配置。
            • RESPONSE_STATUS_NOT_2XX:響應(yīng)狀態(tài)碼不是2xx時(shí)執(zhí)行重試;
            • OCCUR_IO_EXCEPTION:發(fā)生IO異常時(shí)執(zhí)行重試;
            • OCCUR_EXCEPTION:發(fā)生任意異常時(shí)執(zhí)行重試。

          總結(jié)

          今天體驗(yàn)了一把Retrofit,對(duì)比使用HttpUtil,確實(shí)優(yōu)雅不少!通過接口發(fā)起HTTP請(qǐng)求已不再是Feign的專屬,通過Retrofit我們?cè)趩误w應(yīng)用中照樣可以使用這種方式。當(dāng)然retrofit-spring-boot-starter提供的功能遠(yuǎn)不止于此,它還能支持微服務(wù)間的調(diào)用和熔斷降級(jí),感興趣的朋友可以研究下!

          參考資料

          官方文檔:https://github.com/LianjiaTech/retrofit-spring-boot-starter

          項(xiàng)目源碼地址

          https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-retrofit



          瀏覽 22
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  人人模人人插 | 76人少妇精品导航 | 黄色一级大片在线免费看国产 | 日韩va亚洲va欧美va清高 | 无码尤物波多野结衣性开放∧V |