概述
在Spring cloud應用中,當咱們要使用feign客戶端時,通常要作如下三件事情 :java
使用註解@EnableFeignClients啓用feign客戶端;
示例 :spring
@SpringBootApplication
@EnableFeignClients
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
1
2
3
4
5
6
7
使用註解@FeignClient 定義feign客戶端 ;
示例 : 該例子定義了一個feign客戶端,將遠程服務http://test-service/test/echo映射爲一個本地Java方法調用。app
@FeignClient(name = "test-service", path = "/test")
public interface TestService {
@RequestMapping(value = "/echo", method = RequestMethod.GET)
TestModel echo(@RequestParam("parameter") String parameter);
}
1
2
3
4
5
使用註解@Autowired使用上面所定義feign的客戶端 ;負載均衡
@Autowired
TestService testService;框架
public void run()
{
// 這裏的使用本地Java API的方式調用遠程的Restful接口
TestModel dto = testService.echo("Hello,你好!");
log.info("echo : {}", dto);
}
1
2
3
4
5
6
7
8
9
上面的三個步驟,前兩個步驟能夠理解爲定義feign客戶端,第三步是使用所定義的feign客戶端。經過調試發現,上面第三步所注入的testService是一個代理對象,以下所示 :ide
testService = {$Proxy66@5502}
"HardCodedTarget(type=TestService, name=test-service, url=http://test-service/test)"
h = {ReflectiveFeign$FeignInvocationHandler@6924}
target = {Target$HardCodedTarget@6930}
dispatch = {LinkedHashMap@6931} size = 1
0 = {LinkedHashMap$Entry@6948}
"public abstract xxx.model.TestModel xxx.service.TestService.echo(java.lang.String)"
1
2
3
4
5
6
7
該對象會代理客戶端完成遠程服務方法的調用,那麼,該代理對象是如何生成的 ?這篇文章,咱們經過源代碼解析來回答這兩個問題。函數
源代碼解析
源代碼版本 : spring-cloud-openfeign-core-2.1.0.RELEASE , Spring Cloud Greenwich.RELEASE工具
註解@EnableFeignClients:掃描和註冊feign客戶端bean定義
註解@EnableFeignClients用於告訴框架掃描全部經過註解@FeignClient定義的feign客戶端。它又經過註解@Import導入了類FeignClientsRegistrar( feign客戶端註冊器),以下所示:post
@EnableFeignClients
=> @Import(FeignClientsRegistrar.class)
1
2
FeignClientsRegistrar : feign客戶端註冊器
FeignClientsRegistrar實現了接口 ImportBeanDefinitionRegistrar。而ImportBeanDefinitionRegistrar的設計目的,就是被某個實現類實現,配合@Configuration註解的配置類,在配置類被處理時,用於額外註冊一部分bean定義:ui
對於上面的例子,使用者配置類就是 TestApplication
public interface ImportBeanDefinitionRegistrar {
/**
* Register bean definitions as necessary based on the given annotation metadata of
* the importing @Configuration class.
* 根據使用者配置類的註解元數據註冊bean定義
* @param importingClassMetadata 使用者配置類的註解元數據
* @param registry 當前bean定義註冊表,通常指當前Spring應用上下文對象,當前Spring容器
*/
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
#registerBeanDefinitions – 註冊feign客戶端配置和feign客戶端
方法FeignClientsRegistrar#registerBeanDefinitions實現以下:
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {
// 註冊缺省配置到容器 registry
registerDefaultConfiguration(metadata, registry);
// 註冊所發現的各個 feign 客戶端到到容器 registry
registerFeignClients(metadata, registry);
}
1
2
3
4
5
6
7
#registerDefaultConfiguration– 註冊feign客戶端缺省配置
// 註冊feign客戶端的缺省配置,缺省配置信息來自注解元數據的屬性 defaultConfiguration
private void registerDefaultConfiguration(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {
// 獲取註解@EnableFeignClients的註解屬性
Map<String, Object> defaultAttrs = metadata
.getAnnotationAttributes(EnableFeignClients.class.getName(), true);
if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
String name;
// 下面是對所註冊的缺省配置的的命名,格式以下 :
// default.xxx.TestApplication
if (metadata.hasEnclosingClass()) {
// 針對註解元數據metadata對應一個內部類或者方法返回的方法本地類的情形
name = "default." + metadata.getEnclosingClassName();
}
else {
// name 舉例 : default.xxx.TestApplication
// 這裏 xxx.TestApplication 是註解@EnableFeignClients所在配置類的長名稱
name = "default." + metadata.getClassName();
}
registerClientConfiguration(registry, name,
defaultAttrs.get("defaultConfiguration"));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#registerDefaultConfiguration方法最終註冊客戶端缺省配置的動做交給方法#registerDefaultConfiguration執行。
#registerClientConfiguration – 註冊feign客戶端配置
// 將指定feign客戶端配置configuration做爲一個bean定義註冊到容器:
// bean 定義對象類型 : GenericBeanDefinition
// bean class : FeignClientSpecification
// bean name : default.xxx.TestApplication.FeignClientSpecification (缺省配置)
// bean name : test-service.FeignClientSpecification (針對某個feign client 的配置)
private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
Object configuration) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition(FeignClientSpecification.class);
builder.addConstructorArgValue(name);
builder.addConstructorArgValue(configuration);
registry.registerBeanDefinition(
name + "." + FeignClientSpecification.class.getSimpleName(),
builder.getBeanDefinition());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#registerDefaultConfiguration方法用於註冊一個feign客戶端配置bean,能夠用於註冊針對全部feign客戶端的缺省配置的註冊,也能夠用於針對每一個feign客戶端的專有配置的註冊。
針對全部feign客戶端的缺省配置的bean名稱相似於 : default.xxx.TestApplication.FeignClientSpecification,
針對某個名稱爲test-service的feign客戶端的配置的bean名稱相似於:test-service.FeignClientSpecification。
#registerFeignClients – 註冊各個feign客戶端及其配置
// 參數 metadata : 註解@EnableFeignClients所在配置類的註解元數據
public void registerFeignClients(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {
// 定義一個基於classpath的組件掃描器,它會根據指定的掃描位置和@EnableFeignClients註解屬性
// 找出開發人員定義的全部feign客戶端,也就是那些使用了註解@FeignClient的全部接口定義
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
Set<String> basePackages;
// attrs 用於表示註解@EnableFeignClients所在配置類的註解元數據中註解@EnableFeignClients
// 的部分
Map<String, Object> attrs = metadata
.getAnnotationAttributes(EnableFeignClients.class.getName());
AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
FeignClient.class);
final Class<?>[] clients = attrs == null ? null
: (Class<?>[]) attrs.get("clients");
if (clients == null || clients.length == 0) {
// @EnableFeignClients 中沒有指定 clients 屬性的狀況
scanner.addIncludeFilter(annotationTypeFilter);
basePackages = getBasePackages(metadata);
}
else {
// @EnableFeignClients 中指定了 clients 屬性的狀況
final Set<String> clientClasses = new HashSet<>();
basePackages = new HashSet<>();
for (Class<?> clazz : clients) {
basePackages.add(ClassUtils.getPackageName(clazz));
clientClasses.add(clazz.getCanonicalName());
}
AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {
@Override
protected boolean match(ClassMetadata metadata) {
String cleaned = metadata.getClassName().replaceAll("\\$", ".");
return clientClasses.contains(cleaned);
}
};
scanner.addIncludeFilter(
new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
}
// 使用 scanner 掃描每個 basePackage, 獲取其中的 feign 客戶端定義,
// 也就是 @FeignClient 定義的那些接口
for (String basePackage : basePackages) {
Set<BeanDefinition> candidateComponents = scanner
.findCandidateComponents(basePackage);
for (BeanDefinition candidateComponent : candidateComponents) {
if (candidateComponent instanceof AnnotatedBeanDefinition) {
// verify annotated class is an interface
AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
Assert.isTrue(annotationMetadata.isInterface(),
"@FeignClient can only be specified on an interface");
// 獲取所定義的feign客戶端接口上的註解@FeignClient屬性
Map<String, Object> attributes = annotationMetadata
.getAnnotationAttributes(
FeignClient.class.getCanonicalName());
String name = getClientName(attributes);
// 將所定義的feign客戶端上的配置屬性做爲一個bean註冊到容器
registerClientConfiguration(registry, name,
attributes.get("configuration"));
// 將所定義的feign客戶端做爲一個bean註冊到容器:
// bean 定義類型 : GenericBeanDefinition
// bean class : FeignClientFactoryBean
// autowire 模式 : 根據類型綁定
// @FeignClient註解中的url,path,fallback等屬性會設置爲bean定義的屬性
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
}
// 輔助工具類,從@EnableFeignClients註解屬性中獲取basePackages屬性:
// 參考如下@EnableFeignClients註解屬性 :
// 1. value
// 2. basePackages
// 3. basePackageClasses
// 4. 配置類所在的包
// 參數 importingClassMetadata : 使用註解@EnableFeignClients的配置類的元數據
protected Set<String> getBasePackages(AnnotationMetadata importingClassMetadata) {
// 註解@EnableFeignClients的屬性
Map<String, Object> attributes = importingClassMetadata
.getAnnotationAttributes(EnableFeignClients.class.getCanonicalName());
Set<String> basePackages = new HashSet<>();
for (String pkg : (String[]) attributes.get("value")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
for (String pkg : (String[]) attributes.get("basePackages")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
for (Class<?> clazz : (Class[]) attributes.get("basePackageClasses")) {
basePackages.add(ClassUtils.getPackageName(clazz));
}
if (basePackages.isEmpty()) {
basePackages.add(
ClassUtils.getPackageName(importingClassMetadata.getClassName()));
}
return basePackages;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#registerFeignClients 最終註冊feign客戶端配置的動做交給#registerClientConfiguration完成,註冊feign客戶端的動做交給#registerFeignClient方法完成。
#registerFeignClient – 註冊一個feign客戶端
// 將所定義的feign客戶端做爲一個bean註冊到容器:
// bean 定義類型 : GenericBeanDefinition
// bean class : FeignClientFactoryBean -- 這是一個工廠bean,而不是最終bean實例的class
// autowire 模式 : 根據類型綁定
// @FeignClient註解中的url,path,fallback等屬性會設置爲bean定義的屬性
// 參數 registry : Spring 容器
// 參數 annotationMetadata : @FeignClient所註解的接口上的註解元數據
// 參數 attributes : @FeignClient 註解屬性信息
private void registerFeignClient(BeanDefinitionRegistry registry,
AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
String className = annotationMetadata.getClassName();
BeanDefinitionBuilder definition = BeanDefinitionBuilder
.genericBeanDefinition(FeignClientFactoryBean.class);
validate(attributes);
definition.addPropertyValue("url", getUrl(attributes));
definition.addPropertyValue("path", getPath(attributes));
String name = getName(attributes);
definition.addPropertyValue("name", name);
definition.addPropertyValue("type", className);
definition.addPropertyValue("decode404", attributes.get("decode404"));
definition.addPropertyValue("fallback", attributes.get("fallback"));
definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
String alias = name + "FeignClient";
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
boolean primary = (Boolean)attributes.get("primary"); // has a default, won't be null
beanDefinition.setPrimary(primary);
String qualifier = getQualifier(attributes);
if (StringUtils.hasText(qualifier)) {
alias = qualifier;
}
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
new String[] { alias });
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
從上面的代碼分析可知,FeignClientsRegistrar的主要做用以下 :
註冊缺省feign客戶端配置bean定義;
對於每一個@FeignClient註解的feign客戶端定義 :
註冊一個針對該feign客戶端的配置bean定義;
註冊該feign客戶端bean定義,生成bean採用工廠類FeignClientFactoryBean;
並且,上述功能實如今FeignClientsRegistrar實現的接口ImportBeanDefinitionRegistrar所定義的方法registerBeanDefinitions中。而該方法會在應用的@EnableFeignClients註解被處理時被調用執行。具體的執行時調用棧以下所示:
AbstractApplicationContext#invokeBeanFactoryPostProcessors
=> PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors
=> foreach BeanDefinitionRegistryPostProcessor : #postProcessBeanDefinitionRegistry
=> ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
=> #processConfigBeanDefinitions
=> ConfigurationClassBeanDefinitionReader#loadBeanDefinitions
=> foreach ConfigurationClass : #loadBeanDefinitionsForConfigurationClass
=> #loadBeanDefinitionsFromRegistrars
=> foreach ImportBeanDefinitionRegistrar : #registerBeanDefinitions
=> FeignClientsRegistrar#registerBeanDefinitions
1
2
3
4
5
6
7
8
9
10
FeignClientFactoryBean生成feign客戶端代理對象
基於上面的分析,咱們能夠得知,開發人員所定義的feign客戶端和相關配置會以bean定義的形式註冊到bean容器中,這樣當使用@Autowired注入一個feign客戶端時,容器會使用工廠類FeignClientFactoryBean爲其生成一個實例。下面咱們來看其具體工做過程。
FeignClientFactoryBean#getObject生成feign客戶端代理對象
// 該方法由接口FactoryBean約定
@Override
public Object getObject() throws Exception {
return getTarget();
}
<T> T getTarget() {
// 從應用上下文中獲取建立 feign 客戶端的上下文對象 FeignContext
// FeignContext 針對每一個feign客戶端定義會生成一個不一樣的 AnnotationConfigApplicationContext,
// 這些應用上下文的parent都設置爲當前應用的主應用上下文
// 參考 : FeignAutoConfiguration
FeignContext context = applicationContext.getBean(FeignContext.class);
// 爲目標feign客戶端對象構建一個 builder,該builder最終生成的目標feign客戶端是一個
// 動態代理,使用 InvocationHandler : ReflectiveFeign$FeignInvocationHandler
Feign.Builder builder = feign(context);
if (!StringUtils.hasText(this.url)) {
// @FeignClient 屬性 url 屬性沒有指定的狀況
// 根據屬性 name , path 拼裝一個 url,
// 這種一般是須要在多個服務節點之間進行負載均衡的狀況
if (!this.name.startsWith("http")) {
url = "http://" + this.name;
}
else {
url = this.name;
}
// 方法cleanPath()加工屬性path,使其以/開頭,不以/結尾
url += cleanPath();
// 這裏造成的url格式相似 : http://test-service/test
// 其中 test-service 是服務名,不是服務所在節點的IP,主機名或者域名
// 函數loadBalance 作以下動做 :
// 1. 將builder和一個LoadBalancerFeignClient bean實例關聯起來
// 2. 使用一個HystrixTargeter將builder和一個 HardCodedTarget bean實例關聯起來
// 這裏 HardCodedTarget 表示對應 url 爲 http://test-service/test 的遠程服務(可能
// 包含多個服務方法)
// 3. 生成最終的feign client 實例 : ReflectiveFeign$FeignInvocationHandler 的動態代理對象,
// 使用 InvocationHandler : ReflectiveFeign$FeignInvocationHandler。
// 每一個遠程服務方法會對應到一個@FeignClient註解的接口方法上(依據方法上的註解進行匹配)
return (T) loadBalance(builder, context, new HardCodedTarget<>(this.type,
this.name, url));
}
// @FeignClient 屬性 url 屬性被指定的狀況
// 這種一般是明確指出了服務節點的url的狀況,實際上不須要負載均衡
if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
this.url = "http://" + this.url;
}
String url = this.url + cleanPath();
// 將builder和一個LoadBalancerFeignClient bean實例關聯起來
Client client = getOptional(context, Client.class);
if (client != null) {
if (client instanceof LoadBalancerFeignClient) {
// not load balancing because we have a url,
// but ribbon is on the classpath, so unwrap
// 由於指定了明確的服務節點url,因此這裏不須要負載均衡,
// 因此這裏儘管client是LoadBalancerFeignClient,因此
// 實際上能夠獲取其所代理的對象做爲最終的client,
// 至關於去掉了LoadBalancerFeignClient這層的代理功能
client = ((LoadBalancerFeignClient)client).getDelegate();
}
builder.client(client);
}
// 使用一個HystrixTargeter將builder和一個 HardCodedTarget bean實例關聯起來
Targeter targeter = get(context, Targeter.class);
// 生成最終的feign client 實例 : ReflectiveFeign$FeignInvocationHandler 的動態代理對象,
// 使用 InvocationHandler : ReflectiveFeign$FeignInvocationHandler。
// 每一個遠程服務方法會對應到 一個@FeignClient註解的接口方法上(依據方法上的註解進行匹配)
return (T) targeter.target(this, builder, context, new HardCodedTarget<>(
this.type, this.name, url));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
方法FeignClientFactoryBean#feign – 建立feign客戶端構建器
protected Feign.Builder feign(FeignContext context) {
FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
Logger logger = loggerFactory.create(this.type);
// 從上下文獲取一個 Feign.Builder 上,
// 並從上下文得到 Encoder, Decoder, Contract 設置到該 builder 上
Feign.Builder builder = get(context, Feign.Builder.class)
// required values
.logger(logger)
.encoder(get(context, Encoder.class))
.decoder(get(context, Decoder.class))
.contract(get(context, Contract.class));
// 對 builder 進行其餘屬性設置
configureFeign(context, builder);
return builder;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
方法FeignClientFactoryBean#loadBalance – 生成具有負載均衡能力的feign客戶端
爲feign客戶端構建器綁定負載均衡客戶端,綁定目標服務端點,並生成最終的feign客戶端實例。
// 對builder設置負載均衡客戶端,綁定到目標服務端點,構建最終的feign客戶端對象
protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
HardCodedTarget<T> target) {
// 從上下文context獲取一個Client,缺省是 LoadBalancerFeignClient
Client client = getOptional(context, Client.class);
if (client != null) {
// 將client設置到builder上
builder.client(client);
// 從上下文中獲取一個 targeter,缺省是一個 HystrixTargeter
Targeter targeter = get(context, Targeter.class);
// 上面獲取獲得的 targeter 會根據 builder 的類型決定如何將 target
// 綁定到 builder 並設置有關的其餘屬性和功能,而後生成最終的feign客戶端對象
return targeter.target(this, builder, context, target);
}
throw new IllegalStateException(
"No Feign Client for loadBalancing defined. Did you forget to include " +
"spring-cloud-starter-netflix-ribbon?");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
從上面分析能夠看出,缺省狀況下,所使用的feign客戶端構建器類爲Feign.Builder,而且Targeter是一個HystrixTargeter。HystrixTargeter#target方法的參數builder爲Feign.Builder時,會直接調用該builder的target方法,以下所示 :
class HystrixTargeter implements Targeter {
@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
Target.HardCodedTarget<T> target) {
if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
return feign.target(target);
}
// ... 省略其餘代碼
}
}
1
2
3
4
5
6
7
8
9
10
接下來再來看Feign.Builder#target是如何工做的。
// 執行構建而且建立相應的feign客戶端實例
public <T> T target(Target<T> target) {
return build().newInstance(target);
}
// 構建過程,最終根據各類配置生成一個 ReflectiveFeign 對象
public Feign build() {
SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
logLevel, decode404, closeAfterDecode, propagationPolicy);
ParseHandlersByName handlersByName =
new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
errorDecoder, synchronousMethodHandlerFactory);
return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
而後再看ReflectiveFeign#newInstance方法。
// 建立最終的feign客戶端實例 : 一個 ReflectiveFeign$FeignInvocationHandler 的動態代理對象
@Override
public <T> T newInstance(Target<T> target) {
Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
for (Method method : target.type().getMethods()) {
if (method.getDeclaringClass() == Object.class) {
continue;
} else if (Util.isDefault(method)) {
// 對於每一個缺省方法,使用 DefaultMethodHandler
DefaultMethodHandler handler = new DefaultMethodHandler(method);
defaultMethodHandlers.add(handler);
methodToHandler.put(method, handler);
} else {
// 對於每一個對應服務功能端點的方法,缺省使用nameToHandler獲取的MethodHandler,缺省是
// SynchronousMethodHandler
methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
}
}
// 建立feign客戶端實例 ReflectiveFeign$FeignInvocationHandler,
// 該對象包含了上面所建立的methodToHandler,用於對應各個開發者定義的@FeignClient接口方法
InvocationHandler handler = factory.create(target, methodToHandler);
// 建立feign客戶端實例的動態代理對象
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
new Class<?>[] {target.type()}, handler);
// 將缺省方法處理器綁定到feign客戶端實例的動態代理對象上
for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
defaultMethodHandler.bindTo(proxy);
}
return proxy;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
從上面的分析咱們不難看出,爲何最終注入的testService最終是一個ReflectiveFeign$FeignInvocationHandler動態代理實例了。
總結從上面的分析能夠看出,當咱們使用註解@EnableFeignClients 時,至關於啓用了feign客戶端定義的掃描和註冊機制,從而能夠發現開發人員經過註解@FeignClient定義的feign客戶端,並最終做爲bean定義註冊到容器中。而經過@Autowired自動裝配註解,這些feign客戶端會以ReflectiveFeign$FeignInvocationHandler動態代理的形式被注入到使用方。該feign客戶端包含了對每一個接口方法的處理器MethodHandler,接口缺省方法對應DefaultMethodHandler,服務功能端點方法對應SynchronousMethodHandler。