完美解決SpringCloud-OpenFeign使用okhttp替換不生效問題
事發地
原默認的Feign是使用URLConnector進行通信的,當換為okhttp時,直接引入包及配置以下內容根本不生效,還是走原生的。
feign: okhttp: enable: true
事件還原
創建項目並引入pom相關的依賴如下:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <properties> <java.version>1.8</java.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Finchley.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> </project>``
啟動類
@SpringBootApplication @EnableFeignClients public class Ch21OpenFeignApplication { public static void main(String[] args) { SpringApplication.run(Ch21OpenFeignApplication.class, args); } }
配置文件
bootstrap.yml,這裡隻配置是否使用相關的HTTP
logging: level: springcloud.service.HelloFeignService: debug feign: okhttp: enable: true httpclient: enable: false
測試類
Controller
.`@RestController public class HelloFeignController { @Autowired private HelloFeignService helloFeignService; @RequestMapping(value = "/helloFeign",method = RequestMethod.GET) public Object helloFeign(){ return helloFeignService.helloFeign(); } }`
service
@FeignClient(name = "feign-client",url = "http://localhost:8761",fallback = FallbackService.class) public interface HelloFeignService { @RequestMapping(value = "/query/eureka-server",method = RequestMethod.GET) String helloFeign(); }
案件還原
寫好上述的配置與類後,開始DEBUG,下圖是Controller的DEBUG入口:
進入代理類查看HTTPCLIENT使用的是哪一個
到下圖時就要註意瞭,這才是進入使用client的入口
結果看下圖:
參考
經過查找資料,有文章提到是自動配置類的問題FeignAutoConfiguration,文章參考:www.jb51.net/article/206294.htm文章提到,由於@ConditionalOnMissingBean({okhttp3.OkHttpClient.class})導致瞭無法註入OkHttpClient
個人猜測
在隻添加依賴包時,還沒有自行創建okhttp相關的BEAN時,結果如下與添加瞭創建okhttp時一樣。
啟動springboot項目時,BEAN容器化時機不同導致得不到想要的client,在啟動項目時,org.springframework.cloud.openfeign.FeignAutoConfiguration這個配置類是執行後。在BEAN裝載時看到BEAN一級緩存如下圖,並沒有OKHTTP相關的信息。
在這個自動裝配類最下方有一段代碼根本沒有執行,所以從始到終都沒有看到有okHttpClient
@Bean @ConditionalOnMissingBean(Client.class) public Client feignClient() { return new OkHttpClient(this.okHttpClient); }
當我在主類添加以下代碼時,得到的bean如下:
註意:feignClient 這個bean 是一個loadBalancerFeignClient
@Bean public okhttp3.OkHttpClient okHttpClient(){ return new okhttp3.OkHttpClient.Builder() .readTimeout(60, TimeUnit.SECONDS) .connectTimeout(60, TimeUnit.SECONDS) .writeTimeout(120, TimeUnit.SECONDS) .connectionPool(new ConnectionPool()) // .addInterceptor(); .build(); }
當我在主類再追加以下代碼時,得到的bean如下:
註意:feignClient 這個bean,是我們要找的okhttp瞭
@Bean @ConditionalOnMissingBean({Client.class}) public Client feignClient(okhttp3.OkHttpClient client) { return new feign.okhttp.OkHttpClient(client); }
真正的真相
這個要再多看下創建流程,從代碼上分析,這時在有new feign.okhttp.OkHttpClient(client); 這一段代碼,重新把okhttp註入後,才使得feignClient 名稱對應的bean為okhttp。不防從以下代碼進行分析:
LoadBalancerFeignClient 的來源
如下圖,LoadBalancerFeignClient是繼承瞭Client,進入實現類feign.Client.Default,這個類全程都隻有使用HttpURLConnection,所以無論怎樣都隻能獲取到的是默認的JDK裡的http的client
所以這個類創建後要想使用okhttp,那麼就隻能重新創建把舊的bean沖掉,還好,在主類添加的@Bean創建的類正是在完成bean後再執行這裡主類增加的@Bean創建的對象,所以最後加載過程中又會執行bean生命周期的finishBeanFactoryInitialization(beanFactory);
方法所在位置如下:
org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization
註意事項
在主類添加的方法用來創建okhttp時,方法名一定要寫成下圖的這樣,不然創建不瞭feignClient這個bean。
不知道是否有其他方法。這個估計是與org.springframework.cloud.openfeign.FeignAutoConfiguration.OkHttpFeignConfiguration#feignClient有關,相當於創建的方法是一個重寫的過程。
以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。如有錯誤或未考慮完全的地方,望不吝賜教。
推薦閱讀:
- 解決Feign切換client到okhttp無法生效的坑(出現原因說明)
- SpringCloud使用Feign實現動態路由操作
- Feign 使用HttpClient和OkHttp方式
- SpringBoot Java後端實現okhttp3超時設置的方法實例
- SpringCloud openfeign聲明式服務調用實現方法介紹