Feign初始化解析

0 依赖引入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

首先来分析一下pom中如何引入feign的依赖包。如上所示,我这边使用的是spring-cloud的Greenwich.SR2版本,通过spring-cloud-starter-openfeign引入feign,后续分析也基于该版本进行。

那么有必要来看一下spring-cloud-starter-openfeign.pom的定义

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
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign</artifactId>
<version>2.1.2.RELEASE</version>
<relativePath>..</relativePath>
</parent>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<name>Spring Cloud Starter OpenFeign</name>
<description>Spring Cloud Starter OpenFeign</description>
<url>https://projects.spring.io/spring-cloud</url>
<organization>
<name>Pivotal Software, Inc.</name>
<url>https://www.spring.io</url>
</organization>
<properties>
<main.basedir>${basedir}/../..</main.basedir>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-slf4j</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-hystrix</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-java8</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-archaius</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>

根据springboot-starter-*的加载原理(可以参考我之前的文章《Springboot-starter-xxx原理解析》)我们知道springboot加载的是引入包下META-INF/spring.factories文件,我们这里对应的就是spring-cloud-openfeign-core包,来看看其spring.factories的内容。

1
2
3
4
5
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration,\
org.springframework.cloud.openfeign.FeignAutoConfiguration,\
org.springframework.cloud.openfeign.encoding.FeignAcceptGzipEncodingAutoConfiguration,\
org.springframework.cloud.openfeign.encoding.FeignContentGzipEncodingAutoConfiguration

这里的FeignAcceptGzipEncodingAutoConfigurationFeignContentGzipEncodingAutoConfiguration用于请求和响应的压缩暂时不做说明,我们来看看FeignRibbonClientAutoConfigurationFeignAutoConfiguration都做了些什么。

1.1 FeignRibbonClientAutoConfiguration
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
@ConditionalOnClass({ ILoadBalancer.class, Feign.class })
@Configuration
@AutoConfigureBefore(FeignAutoConfiguration.class)
@EnableConfigurationProperties({ FeignHttpClientProperties.class })
@Import({ HttpClientFeignLoadBalancedConfiguration.class,
OkHttpFeignLoadBalancedConfiguration.class,
DefaultFeignLoadBalancedConfiguration.class })
public class FeignRibbonClientAutoConfiguration {

@Bean
@Primary
@ConditionalOnMissingBean
@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
public CachingSpringLoadBalancerFactory cachingLBClientFactory(
SpringClientFactory factory) {
return new CachingSpringLoadBalancerFactory(factory);
}

@Bean
@Primary
@ConditionalOnMissingBean
@ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
public CachingSpringLoadBalancerFactory retryabeCachingLBClientFactory(
SpringClientFactory factory, LoadBalancedRetryFactory retryFactory) {
return new CachingSpringLoadBalancerFactory(factory, retryFactory);
}

@Bean
@ConditionalOnMissingBean
public Request.Options feignRequestOptions() {
return LoadBalancerFeignClient.DEFAULT_OPTIONS;
}

}

FeignHttpClientProperties定义了http请求的默认参数,包括最大连接数、连接超时时间等。

通过@AutoConfigureBefore注解我们可以看到该类的初始化要求是在FeignAutoConfiguration之前的。

通过@Import注解引入了HttpClientFeignLoadBalancedConfigurationOkHttpFeignLoadBalancedConfigurationDefaultFeignLoadBalancedConfiguration几个配置类,但前两个类是包含条件注解@ConditionalOnClass的,对应条件分别是ApacheHttpClient.classOkHttpClient.class,也就是当选择http实现为ApacheHttpClient和OkHttpClient时才会触发初始化,当我们选择默认的http实现时,仅会触发DefaultFeignLoadBalancedConfiguration的初始化,那么来看一下该类:

1
2
3
4
5
6
7
8
9
10
11
12
@Configuration
class DefaultFeignLoadBalancedConfiguration {

@Bean
@ConditionalOnMissingBean
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory) {
return new LoadBalancerFeignClient(new Client.Default(null, null), cachingFactory,
clientFactory);
}

}

这里实例化了feignClient为一个LoadBalancerFeignClient类型的对象。

最后看一下cachingLBClientFactoryretryabeCachingLBClientFactory的初始化,根据@ConditionalOnMissingClass@ConditionalOnClass值均为org.springframework.retry.support.RetryTemplate可以看出这两个类是互斥的,根据RetryTemplate类的存在与否仅会有一个bean被实例化。RetryTemplate是spring5+版本增加的功能,需要另外引入spring-retry包。

1.2 FeignAutoConfiguration
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
@Configuration
@ConditionalOnClass(Feign.class)
@EnableConfigurationProperties({ FeignClientProperties.class,
FeignHttpClientProperties.class })
public class FeignAutoConfiguration {

@Autowired(required = false)
private List<FeignClientSpecification> configurations = new ArrayList<>();

@Bean
public HasFeatures feignFeature() {
return HasFeatures.namedFeature("Feign", Feign.class);
}

@Bean
public FeignContext feignContext() {
FeignContext context = new FeignContext();
context.setConfigurations(this.configurations);
return context;
}

@Configuration
@ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
protected static class HystrixFeignTargeterConfiguration {

@Bean
@ConditionalOnMissingBean
public Targeter feignTargeter() {
return new HystrixTargeter();
}

}

@Configuration
@ConditionalOnMissingClass("feign.hystrix.HystrixFeign")
protected static class DefaultFeignTargeterConfiguration {

@Bean
@ConditionalOnMissingBean
public Targeter feignTargeter() {
return new DefaultTargeter();
}

}

// the following configuration is for alternate feign clients if
// ribbon is not on the class path.
// see corresponding configurations in FeignRibbonClientAutoConfiguration
// for load balanced ribbon clients.
@Configuration
@ConditionalOnClass(ApacheHttpClient.class)
@ConditionalOnMissingClass("com.netflix.loadbalancer.ILoadBalancer")
@ConditionalOnMissingBean(CloseableHttpClient.class)
@ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
protected static class HttpClientFeignConfiguration {

private final Timer connectionManagerTimer = new Timer(
"FeignApacheHttpClientConfiguration.connectionManagerTimer", true);

@Autowired(required = false)
private RegistryBuilder registryBuilder;

private CloseableHttpClient httpClient;

@Bean
@ConditionalOnMissingBean(HttpClientConnectionManager.class)
public HttpClientConnectionManager connectionManager(
ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
FeignHttpClientProperties httpClientProperties) {
final HttpClientConnectionManager connectionManager = connectionManagerFactory
.newConnectionManager(httpClientProperties.isDisableSslValidation(),
httpClientProperties.getMaxConnections(),
httpClientProperties.getMaxConnectionsPerRoute(),
httpClientProperties.getTimeToLive(),
httpClientProperties.getTimeToLiveUnit(),
this.registryBuilder);
this.connectionManagerTimer.schedule(new TimerTask() {
@Override
public void run() {
connectionManager.closeExpiredConnections();
}
}, 30000, httpClientProperties.getConnectionTimerRepeat());
return connectionManager;
}

@Bean
public CloseableHttpClient httpClient(ApacheHttpClientFactory httpClientFactory,
HttpClientConnectionManager httpClientConnectionManager,
FeignHttpClientProperties httpClientProperties) {
RequestConfig defaultRequestConfig = RequestConfig.custom()
.setConnectTimeout(httpClientProperties.getConnectionTimeout())
.setRedirectsEnabled(httpClientProperties.isFollowRedirects())
.build();
this.httpClient = httpClientFactory.createBuilder()
.setConnectionManager(httpClientConnectionManager)
.setDefaultRequestConfig(defaultRequestConfig).build();
return this.httpClient;
}

@Bean
@ConditionalOnMissingBean(Client.class)
public Client feignClient(HttpClient httpClient) {
return new ApacheHttpClient(httpClient);
}

@PreDestroy
public void destroy() throws Exception {
this.connectionManagerTimer.cancel();
if (this.httpClient != null) {
this.httpClient.close();
}
}

}

@Configuration
@ConditionalOnClass(OkHttpClient.class)
@ConditionalOnMissingClass("com.netflix.loadbalancer.ILoadBalancer")
@ConditionalOnMissingBean(okhttp3.OkHttpClient.class)
@ConditionalOnProperty("feign.okhttp.enabled")
protected static class OkHttpFeignConfiguration {

private okhttp3.OkHttpClient okHttpClient;

@Bean
@ConditionalOnMissingBean(ConnectionPool.class)
public ConnectionPool httpClientConnectionPool(
FeignHttpClientProperties httpClientProperties,
OkHttpClientConnectionPoolFactory connectionPoolFactory) {
Integer maxTotalConnections = httpClientProperties.getMaxConnections();
Long timeToLive = httpClientProperties.getTimeToLive();
TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit();
return connectionPoolFactory.create(maxTotalConnections, timeToLive, ttlUnit);
}

@Bean
public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory,
ConnectionPool connectionPool,
FeignHttpClientProperties httpClientProperties) {
Boolean followRedirects = httpClientProperties.isFollowRedirects();
Integer connectTimeout = httpClientProperties.getConnectionTimeout();
Boolean disableSslValidation = httpClientProperties.isDisableSslValidation();
this.okHttpClient = httpClientFactory.createBuilder(disableSslValidation)
.connectTimeout(connectTimeout, TimeUnit.MILLISECONDS)
.followRedirects(followRedirects).connectionPool(connectionPool)
.build();
return this.okHttpClient;
}

@PreDestroy
public void destroy() {
if (this.okHttpClient != null) {
this.okHttpClient.dispatcher().executorService().shutdown();
this.okHttpClient.connectionPool().evictAll();
}
}

@Bean
@ConditionalOnMissingBean(Client.class)
public Client feignClient(okhttp3.OkHttpClient client) {
return new OkHttpClient(client);
}

}

}

接着来看FeignAutoConfiguration类,这里牵涉到4个内部类的初始化,其中HttpClientFeignConfigurationOkHttpFeignConfiguration对应需要ApacheHttpClient.classOkHttpClient.class类的存在,因此在默认情况下并不会执行初始化,暂不做分析。接着看一下HystrixFeignTargeterConfigurationDefaultFeignTargeterConfiguration类,同样是根据feign.hystrix.HystrixFeign类存在与否,二选一进行初始化。HystrixFeign这个类在jar包feign.hystrix下,而spring-cloud-starter-openfeign.pom中正好有引入该包,那么这里很自然的就会选择初始化HystrixFeignTargeterConfiguration类,同时初始化feignTargeter为一个HystrixTargeter类型的对象。

2 FeignClientsConfiguration

除了上述加载的类以外,spring-cloud-openfeign-core包下还有一个类也会自动被加载,它就是FeignClientsConfiguration,先来看一下其定义:

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
@Configuration
public class FeignClientsConfiguration {

***部分省略***

@Bean
@ConditionalOnMissingBean
public Retryer feignRetryer() {
return Retryer.NEVER_RETRY;
}

@Bean
@Scope("prototype")
@ConditionalOnMissingBean
public Feign.Builder feignBuilder(Retryer retryer) {
return Feign.builder().retryer(retryer);
}

@Configuration
@ConditionalOnClass({ HystrixCommand.class, HystrixFeign.class })
protected static class HystrixFeignConfiguration {

@Bean
@Scope("prototype")
@ConditionalOnMissingBean
@ConditionalOnProperty(name = "feign.hystrix.enabled")
public Feign.Builder feignHystrixBuilder() {
return HystrixFeign.builder();
}

}

}

可以看到,由于@Configuration注解的缘故,该类也会在初始化时被Spring加载,同时其内部类HystrixFeignConfiguration也会被加载,只要引入了对应的类HystrixCommandHystrixFeign,而这两个类分属于com.netflix.hystrixfeign.hystrix包,这在之前的spring-cloud-starter-openfeign.pom中都有引入。另外需要注意的是feign.hystrix.enabled这个配置,只有当显式设置为true时,才会触发feignHystrixBuilder()的初始化。另外需要注意的是feignBuilder方法,当feign.hystrix.enabled不为true时,Feign.Builder会由该方法来初始化。

那么来具体看一下HystrixFeign.builder()做了什么。

1
2
3
public static Builder builder() {
return new Builder();
}

这里的BuilderHystrixFeign类的内部类,继承自Feign.Builder

3 启动注解

1
2
3
4
5
6
7
8
9
10
@Slf4j
@SpringBootApplication
@EnableFeignClients
public class HuiMallOrderApplication {
public static void main(String[] args) {
log.info("开始启动");
SpringApplication.run(HuiMallOrderApplication.class, args);
log.info("启动完成");
}
}

除了引入feign依赖之外,启动类上还需要添加注解@EnableFeignClients,那么我们还得来看看EnableFeignClients的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {

String[] value() default {};
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<?>[] defaultConfiguration() default {};
Class<?>[] clients() default {};

}

这里要关注的是@Import(FeignClientsRegistrar.class),将FeignClientsRegistrar注册为了bean,我们来看看该类的定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {

private ResourceLoader resourceLoader;
private Environment environment;

@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}

@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}

@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
registerDefaultConfiguration(metadata, registry);
registerFeignClients(metadata, registry);
}

***部分省略***
}

该类实现了spring的3个接口ImportBeanDefinitionRegistrarResourceLoaderAwareEnvironmentAware,根据spring的实现机制,该类初始化时会调用对应的方法实现。其中setResourceLoadersetEnvironment方法实现较为简单,重点需要关注的是registerBeanDefinitions方法,该方法的入参AnnotationMetadata为我们的启动类HuiMallOrderApplication上的注解对应信息,BeanDefinitionRegistry为所有bean的注册相关信息。

接着分别来看一下registerDefaultConfigurationregisterFeignClients方法的实现:

3.1 registerDefaultConfiguration
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private void registerDefaultConfiguration(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
Map<String, Object> defaultAttrs = metadata
.getAnnotationAttributes(EnableFeignClients.class.getName(), true);

if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
String name;
if (metadata.hasEnclosingClass()) {
name = "default." + metadata.getEnclosingClassName();
}
else {
name = "default." + metadata.getClassName();
}
registerClientConfiguration(registry, name,
defaultAttrs.get("defaultConfiguration"));
}
}

metadata.getAnnotationAttributes(EnableFeignClients.class.getName(), true)方法返回EnableFeignClients类的属性列表,包括了defaultConfiguration属性,因此会进入执行if分支的内容。接着来看一下registerClientConfiguration方法:

1
2
3
4
5
6
7
8
9
10
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());
}

该类主要做的事情是将@EnableFeignClients中自定义的defaultConfiguration值注册到registry中,这里注册的beanName为default+类全名,而bean定义为FeignClientSpecification的构造函数参数中增加对应的nameconfigure值。

3.2 registerFeignClients
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
public void registerFeignClients(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);

Set<String> basePackages;

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) {
//扫描带有FeignClient注解的类
scanner.addIncludeFilter(annotationTypeFilter);
basePackages = getBasePackages(metadata);
}
else {
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)));
}

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");
// 获取@FeignClient注解上的各个属性值
Map<String, Object> attributes = annotationMetadata
.getAnnotationAttributes(
FeignClient.class.getCanonicalName());

String name = getClientName(attributes);
registerClientConfiguration(registry, name,
attributes.get("configuration"));

registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
}

这里的ClassPathScanningCandidateComponentProvider是Spring提供的工具,用于查找classpath下符合要求的class文件。该方法看着很长,其实做的事情就是初始化scanner并扫描指定包路径下包含@FeignClient注解的类。并将每一个类通过registerClientConfiguration方法注册到registry中。这里有两个方法需要关注一下,分别是getClientName(attributes)registerFeignClient(registry, annotationMetadata, attributes)

先看getClientName(attributes)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private String getClientName(Map<String, Object> client) {
if (client == null) {
return null;
}
String value = (String) client.get("contextId");
if (!StringUtils.hasText(value)) {
value = (String) client.get("value");
}
if (!StringUtils.hasText(value)) {
value = (String) client.get("name");
}
if (!StringUtils.hasText(value)) {
value = (String) client.get("serviceId");
}
if (StringUtils.hasText(value)) {
return value;
}

throw new IllegalStateException("Either 'name' or 'value' must be provided in @"
+ FeignClient.class.getSimpleName());
}

该方法用于生成注册到registry的name,可以看到getClientName对name的取值优先级依次是contextId->value->name->serviceId,根据取得的name去注册registry

接着来看registerFeignClient(registry, annotationMetadata, attributes)

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
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);
String contextId = getContextId(attributes);
definition.addPropertyValue("contextId", contextId);
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 = contextId + "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);
}

这个方法主要做的事情是从attributes中将之前@FeignClient注解上获取的属性都添加到attributes对象上,根据attributes对象生成BeanDefinitionHolder对象,这里需要关注的是definition对象的初始化是基于FeignClientFactoryBean的,该类定义为:

1
class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean, ApplicationContextAware

因为实现了FactoryBean<Object>接口的关系,所以该类实现了getObject()方法,因此基于FeignClientFactoryBean创建的bean对象的注入会依赖于该方法,此处暂不做深入分析。

BeanDefinitionHolder对象创建完成后,最后调用了BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry)方法完成对象的注册,来看看该方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {

// Register bean definition under primary name.
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

// Register aliases for bean name, if any.
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}

registerBeanDefinition方法之前已经见到过了,所以这里做的事情其实就是把生成的生成BeanDefinitionHolder对象注册到registry上,同时注册其对应的别名alias

到此feign相关的整个初始化工作也就算是完成了。

4 总结

本文梳理了feign在项目中的基本初始化流程,了解了feign中不同条件下初始化时产生的不同变化,但并未涉及基于feign进行接口调用时的源码分析,这会在后续的文章中补上。