Feign原始碼解析:初始化過程(二)

三國夢迴發表於2023-12-23

背景

上一篇介紹了Feign原始碼初始化的一部分,內容主要是,@EnableFeignClients、@FeignClient這些註解,都支援設定一些自定義的配置類:

A custom @Configuration for all feign clients. Can contain override @Bean definition
 for the pieces that make up the client, for instance feign.codec.Decoder, 
feign.codec.Encoder, feign.Contract.

每個被@EnableFeignClients、@FeignClient註解的類都會對應生成一個bean,型別為:org.springframework.cloud.openfeign.FeignClientSpecification:

public class FeignClientSpecification implements NamedContextFactory.Specification {

	private String name;

	private Class<?>[] configuration;

即使沒定義自定義的配置類,這個bean照樣生成,只是裡面的configuration欄位是null。

這些bean都不是spring boot那種自動裝配類,因為自動裝配類一般來說,都是帶條件的,比如要檢測到classpath中有某個類,某個property的值等於xxx。

今天,就簡單介紹下,Feign啟動過程中,自動裝配的那些類。

專案簡介

自動裝配有很多條件都是基於類是否存在來判斷,我們們先看看classpath中有啥,主要就是web、nacos服務發現、feign、loadbalancer。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

當然,這裡是要透過nacos進行服務發現的:

spring.cloud.nacos.discovery.username=111
spring.cloud.nacos.discovery.password=222
spring.cloud.nacos.discovery.server-addr=1.1.1.1:8848
spring.cloud.nacos.discovery.namespace=test

Feign呼叫的client程式碼:

package com.example.demo.demos.nacosdiscoveryconsumer;

import org.springframework.cloud.openfeign.FeignClient;

@FeignClient("echo-service-provider") 
public interface EchoService {

    @GetMapping("/echo/{message}")
    String echo(@PathVariable("message") String message);
}

手動梳理裝配類

稍微瞭解spring boot的自動裝配的話,大概知道,在starter那些依賴中,jar檔案一般沒有實質內容,沒有class啥的,主要的內容還是pom檔案,裡面定義了該starter依賴的那些jar:

image-20231223214040162

pom依賴主要包含feign自身、spring對feign的整合、spring-loadbalancer:

<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-core</artifactId>
    <version>11.10</version>
    <scope>compile</scope>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-openfeign-core</artifactId>
    <version>3.1.7</version>
    <scope>compile</scope>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
    <version>3.1.6</version>
    <scope>compile</scope>
    <optional>true</optional>
</dependency>

我們看看spring-cloud-openfeign-core這個依賴,這個spring-cloud-openfeign-core-3.1.7.pom呢,裡面定義了很多底層依賴,而spring-cloud-openfeign-core-3.1.7.jar,則不再是無實質內容了:

image-20231223214602025

大家看到我上圖框的spring.factories檔案,大概就知道,這個東西是和自動裝配有關係的。

我們開啟看看:

image-20231223214714380

裡面主要就是定義了,需要自動裝配的配置類。

比如第一個類:org.springframework.cloud.openfeign.hateoas.FeignHalAutoConfiguration

image-20231223214909861

這些類得特徵是:都是有條件的,這也符合自動裝配的邏輯,自動裝配就是猜測你需要某些類,猜測那是需要依據的,依據就是:檢查你的各種上下文,就跟現在那些短影片推薦一樣的,猜你喜歡嘛。

這邊簡單彙總下,就是這5個自動裝配類:

org.springframework.cloud.openfeign.hateoas.FeignHalAutoConfiguration,\
org.springframework.cloud.openfeign.FeignAutoConfiguration,\
org.springframework.cloud.openfeign.encoding.FeignAcceptGzipEncodingAutoConfiguration,\
org.springframework.cloud.openfeign.encoding.FeignContentGzipEncodingAutoConfiguration,\
org.springframework.cloud.openfeign.loadbalancer.FeignLoadBalancerAutoConfiguration

接下來,看看spring-loadbalancer那個依賴,盤一盤它:

spring-cloud-starter-loadbalancer-3.1.6.jar,和其他starter一樣,裡面啥都沒有;

spring-cloud-starter-loadbalancer-3.1.6.pom,主要依賴如下:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-loadbalancer</artifactId>
    <version>3.1.6</version>
    <scope>compile</scope>
</dependency>

該依賴如下:

image-20231223215630377

主要包含如下幾個自動裝配類:

org.springframework.cloud.loadbalancer.config.LoadBalancerAutoConfiguration,\
org.springframework.cloud.loadbalancer.config.BlockingLoadBalancerClientAutoConfiguration,\
org.springframework.cloud.loadbalancer.config.LoadBalancerCacheAutoConfiguration,\
org.springframework.cloud.loadbalancer.security.OAuth2LoadBalancerClientAutoConfiguration,\
org.springframework.cloud.loadbalancer.config.LoadBalancerStatsAutoConfiguration

至於nacos,也是一樣的套路盤起來,但是,它要直接一點,直接starter裡面就是實質內容了:

image-20231223215905202

引入的自動配置類有:

  com.alibaba.cloud.nacos.discovery.NacosDiscoveryAutoConfiguration,\
  com.alibaba.cloud.nacos.endpoint.NacosDiscoveryEndpointAutoConfiguration,\
  com.alibaba.cloud.nacos.registry.NacosServiceRegistryAutoConfiguration,\
  com.alibaba.cloud.nacos.discovery.NacosDiscoveryClientConfiguration,\
  com.alibaba.cloud.nacos.discovery.reactive.NacosReactiveDiscoveryClientConfiguration,\
  com.alibaba.cloud.nacos.discovery.configclient.NacosConfigServerAutoConfiguration,\
  com.alibaba.cloud.nacos.loadbalancer.LoadBalancerNacosAutoConfiguration,\
  com.alibaba.cloud.nacos.NacosServiceAutoConfiguration,\
  com.alibaba.cloud.nacos.util.UtilIPv6AutoConfiguration

但以上就完了嗎,不是。spring-cloud-loadbalancer是屬於spring-cloud-commons的,在commons的jar包中,也有相關的自動配置類:

image-20231223223204449

org.springframework.cloud.client.loadbalancer.AsyncLoadBalancerAutoConfiguration,\
org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration,\
org.springframework.cloud.client.loadbalancer.LoadBalancerDefaultMappingsProviderAutoConfiguration,\
org.springframework.cloud.client.loadbalancer.reactive.LoadBalancerBeanPostProcessorAutoConfiguration,\
org.springframework.cloud.client.loadbalancer.reactive.ReactorLoadBalancerClientAutoConfiguration,\

這邊彙總下吧:

org.springframework.cloud.openfeign.hateoas.FeignHalAutoConfiguration,\
org.springframework.cloud.openfeign.FeignAutoConfiguration,\
org.springframework.cloud.openfeign.encoding.FeignAcceptGzipEncodingAutoConfiguration,\
org.springframework.cloud.openfeign.encoding.FeignContentGzipEncodingAutoConfiguration,\
org.springframework.cloud.openfeign.loadbalancer.FeignLoadBalancerAutoConfiguration

org.springframework.cloud.loadbalancer.config.LoadBalancerAutoConfiguration,\
org.springframework.cloud.loadbalancer.config.BlockingLoadBalancerClientAutoConfiguration,\
org.springframework.cloud.loadbalancer.config.LoadBalancerCacheAutoConfiguration,\
org.springframework.cloud.loadbalancer.security.OAuth2LoadBalancerClientAutoConfiguration,\
org.springframework.cloud.loadbalancer.config.LoadBalancerStatsAutoConfiguration


com.alibaba.cloud.nacos.discovery.NacosDiscoveryAutoConfiguration,\
com.alibaba.cloud.nacos.endpoint.NacosDiscoveryEndpointAutoConfiguration,\
com.alibaba.cloud.nacos.registry.NacosServiceRegistryAutoConfiguration,\
com.alibaba.cloud.nacos.discovery.NacosDiscoveryClientConfiguration,\
com.alibaba.cloud.nacos.discovery.reactive.NacosReactiveDiscoveryClientConfiguration,\
com.alibaba.cloud.nacos.discovery.configclient.NacosConfigServerAutoConfiguration,\
com.alibaba.cloud.nacos.loadbalancer.LoadBalancerNacosAutoConfiguration,\
com.alibaba.cloud.nacos.NacosServiceAutoConfiguration,\
com.alibaba.cloud.nacos.util.UtilIPv6AutoConfiguration

org.springframework.cloud.client.loadbalancer.AsyncLoadBalancerAutoConfiguration,\
org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration,\
org.springframework.cloud.client.loadbalancer.LoadBalancerDefaultMappingsProviderAutoConfiguration,\
org.springframework.cloud.client.loadbalancer.reactive.LoadBalancerBeanPostProcessorAutoConfiguration,\
org.springframework.cloud.client.loadbalancer.reactive.ReactorLoadBalancerClientAutoConfiguration,\

這一下,就是幾十個自動裝配類,真他麼多。

注意,這裡面還有兩個名字相同,包名不同的:

image-20231223223506657

自動配置類最終引入了哪些bean

自動裝配類都是相當複雜的,基於各種條件的計算,很多都不是一眼就能看出來的,有些和順序還息息相關,比如,ConditionalOnMissingBean,這個就很有意思,在沒有bean存在的情況下才自動裝配,但我之前遇到過,有兩個自動裝配類,都加了這個註解,那,最終到底是哪個自動裝配進去呢?

所以,如果專案複雜,可以考慮開啟如下日誌開關:

logging.level.org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener=DEBUG

就會列印如下的日誌,哪些裝配了,哪些沒裝配,一目瞭然:

image-20231223220710773

spring boot actuator的actuator/conditions也支援動態檢視這個資訊,甚至可以看到各個spring容器的:

image-20231223221027037

如果想知道某個自動裝配類中,哪些bean匹配了,哪些bean沒匹配上,只能ctrl + f了,比如Feign這個裝配類:

org.springframework.cloud.openfeign.FeignAutoConfiguration

image-20231223221733255

相當於匹配上了如下兩個bean:

image-20231223222023927

這邊累計彙總下,裝配成功的:

  • org.springframework.cloud.openfeign.FeignAutoConfiguration及內部的:
    FeignAutoConfiguration、
    FeignAutoConfiguration.DefaultFeignTargeterConfiguration
    FeignAutoConfiguration.DefaultFeignTargeterConfiguration#feignTargeter

  • org.springframework.cloud.openfeign.loadbalancer.FeignLoadBalancerAutoConfiguration,內部類/method:無,這個類是靠import引入其他configuration的

  • org.springframework.cloud.loadbalancer.config.LoadBalancerAutoConfiguration

    LoadBalancerAutoConfiguration#zoneConfig

  • org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration

    org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration#loadBalancerRequestFactory

    org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration#loadBalancerRequestFactory

    org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration.LoadBalancerInterceptorConfig

    org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration.LoadBalancerInterceptorConfig#restTemplateCustomizer

  • BlockingLoadBalancerClientAutoConfiguration

    BlockingLoadBalancerClientAutoConfiguration#blockingLoadBalancerClient

    BlockingLoadBalancerClientAutoConfiguration#loadBalancerServiceInstanceCookieTransformer

    BlockingLoadBalancerClientAutoConfiguration#xForwarderHeadersTransformer

  • LoadBalancerCacheAutoConfiguration

    內部略,太多了,寫了也記不住

  • NacosDiscoveryAutoConfiguration

    NacosDiscoveryAutoConfiguration#nacosProperties

    NacosDiscoveryAutoConfiguration#nacosServiceDiscovery

  • NacosServiceRegistryAutoConfiguration

    NacosServiceRegistryAutoConfiguration#nacosAutoServiceRegistration

    NacosServiceRegistryAutoConfiguration#nacosRegistration

  • NacosDiscoveryClientConfiguration

  • NacosServiceAutoConfiguration

  • UtilIPv6AutoConfiguration

  • AsyncLoadBalancerAutoConfiguration

  • LoadBalancerDefaultMappingsProviderAutoConfiguration

以上都是匹配上的,沒匹配的都沒寫。這邊寫了一抹多,供查閱吧,重點的有一個要先摘出來說,它是屬於沒匹配上的:

LoadBalancerNacosAutoConfiguration:
      Did not match:
         - @ConditionalOnProperty (spring.cloud.loadbalancer.nacos.enabled=true) did not find property 'spring.cloud.loadbalancer.nacos.enabled' (OnPropertyCondition)

這是一個nacos包裡的關於loadbalancer的自動配置類,當初就是因為這個類,讓我遇到了些問題,才好好研究了下feign,寫了這幾篇,可以說的上是為了這盤醋包了這頓餃子,後面的文章會再說說這個類。

相關文章