一:使用方法
1,添加依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath />
</parent>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-cloud.version>Hoxton.RELEASE</spring-cloud.version>
</properties>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
2,在springboot的启动类上面添加注解
@SpringBootApplication
//当程序启动时会扫描所有带有@FeignClient注解的类进行处理
@EnableFeignClients
public class Application
{
public static void main( String[] args) {
SpringApplication.run(Application.class, args) ;
}
}
3,添加配置
server.port=8011
spring.application.name=feign-app
#开启okhttp为feign的默认Client
feign.httpclient.enable=false
feign.okhttp.enable=true
feign.client.config.<feignName>.connectTimeout=5000 #连接超时时间
feign.client.config.<feignName>.readTimeout=5000 #读超时时间
feign.client.config.<feignName>.loggerLevel=full #Feign的日志级别
feign.clinet.config.default.connectTimeout=5000
feign.clinet.config.default.readTimeout=5000
feign.clinet.config.default.loggerLevel=full
ribbon.ConnectTimeout=30000 #请求连接的超时时间
ribbon.ReadTimeout=120000 #请求处理的超时时间
feign.httpclient.enabled=true
feign.okhttp.enabled=false
feign.hystrix.enabled=true #hystrix熔断机制
hystrix.shareSecurityContext=true
hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds=100000
hystrix.command.default.circuitBreaker.forceClosed=true
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=600000
4,添加Feign客户端
/**
* name : 指定Feign的名称。如果使用了Ribbon,该属性会作为微服务名称用户服务发现
* path: 定义统一的前缀
* url: 一般用于调试,手动指定调用的地址
* */
@FeignClient(name="hostNmae" )
public interface FeginClientService {
@RequestMapping(method=RequestMethod.GET,path= {"/route/balance/access/query"})
public String getMsg() ;
}
二:源码分析
1,@EnableFeignClients
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
/**
* @return the array of 'basePackages'.
*/
String[] value() default {};
/**
* Base packages to scan for annotated components.
* @return the array of 'basePackages'.
*/
String[] basePackages() default {};
/**
* @return the array of 'basePackageClasses'.
*/
Class<?>[] basePackageClasses() default {};
/**
* @see FeignClientsConfiguration for the defaults
* @return list of default configurations
*/
Class<?>[] defaultConfiguration() default {};
/**
* List of classes annotated with @FeignClient. If not empty, disables classpath
*/
Class<?>[] clients() default {};
}
该注解会导入FeignClientsRegistrar类进行FeginClient的配置,其代码如下
class FeignClientsRegistrar
implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
private ResourceLoader resourceLoader;
private Environment environment;
FeignClientsRegistrar() {
}
//被初始化调用
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
//基于注解添加默认的配置。由于在@EnableFeignClients并未设置,所以方法内并不会真正执行某些动作
registerDefaultConfiguration(metadata, registry);
//基于注解配置每个Feign客户端
registerFeignClients(metadata, registry);
}
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();
}
//注册默认的Feign客户端配置
registerClientConfiguration(registry, name,
defaultAttrs.get("defaultConfiguration"));
}
}
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());
//设置注解的过滤器,筛选出@FeignClient
AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
FeignClient.class);
final Class<?>[] clients = attrs == null ? null
: (Class<?>[]) attrs.get("clients");
if (clients == null || clients.length == 0) {
/**
* 如果在@FeignClients中没有配置FeignClient,设置过滤器并得到@EnableFeignClients中value、
* basePackages、basePackageClasses属性的取值并放入集合,如果集合为空,
* 则返回@FeignClients注解所在的包路径
*/
scanner.addIncludeFilter(annotationTypeFilter);
basePackages = getBasePackages(metadata);
} else {
//用@FeignClient注释的类列表,如果不为空,则禁用类路径扫描。
final Set<String> clientClasses = new HashSet<>();
basePackages = new HashSet<>();
//遍历@FeignClients中配置的各个客户端
for (Class<?> clazz : clients) {
//添加各个Feign客户端的包路径
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");
Map<String, Object> attributes = annotationMetadata
.getAnnotationAttributes(
FeignClient.class.getCanonicalName());
String name = getClientName(attributes);
//添加每个Fegin客户端的配置信息
registerClientConfiguration(registry, name,
attributes.get("configuration"));
//注册每个Fegin客户端
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
}
private void registerFeignClient(BeanDefinitionRegistry registry,
AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
String className = annotationMetadata.getClassName();
//这里是重点,当执行注入的时候会被调用,定义Bean的工厂[见下面的源码]
BeanDefinitionBuilder definition = BeanDefinitionBuilder
.genericBeanDefinition(FeignClientFactoryBean.class);
validate(attributes);
definition.addPropertyValue("url", getUrl(attributes));
definition.addPropertyValue("path", getPath(attributes));
//依次获取serviceId、name、value的值,取到第一个不为空为止
String name = getName(attributes);
definition.addPropertyValue("name", name);
//ApplicationContext的标识【从FeignContext中得到该客户端的应用上下文】。
//首先获取注解上的contextId属性,没有则依次获取serviceId、name、value的值
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;
}
//持有BeanDefinition、BeanDefinition的名称,BeanDefinition的别名
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
new String[] { alias });
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
}
到此为止,主要完成了几件事
1)读取@EnableFeignClients中配置的全局Fegin客户端的默认配置(defaultConfiguration),并生成名为“.default”开头,类名(配置类全路径)结尾的FeignClientSpecification实例Bean
2)读取@EnableFeignClients中配置的clients属性,如果为空则扫描value或basePackages或basePackageClasses属性指定的类路径得到使用@FeignClient注解标注的接口; 否则遍历每个clients,将每个client的包路径收集起来作为basePackages,然后再扫描一次这些路径得到指定的这些clients【@FeignClient标注的接口】
3)解析每个@FeignClient标注的接口,获取@FeignClient的configuration值(可能为空),然后生成FeignClientSpecification实例Bean(其Bean的名称为@FeignClient中contextId或value或name或serviceId的值); 同时为该@FeignClient标注的接口创建FeignClientFactoryBean实例对(其Bean的名字为配置的qualifier属性,如果该属性为空则以属性contextId或serviceId或name或value的值【如果为http开头的url则只取host】开头,以“FeignClient”结尾的字符串作为Bean的名字。
注意:该FeignClientFactoryBean工厂实例是运行时的核心。
FeignClientFactoryBean【源码摘选】
通过此类我们可以看到被注入的Feign客户端被封装成了什么样。
class FeignClientFactoryBean
implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {
@Override
public Object getObject() throws Exception {
return getTarget();
}
//从this.contextId所指定名称的ApplicationContext中得到类型为type的Bean
protected <T> T getOptional(FeignContext context, Class<T> type) {
return context.getInstance(this.contextId, type);
}
/**
* @param <T> the target type of the Feign client
* @return a {@link Feign} client created with the specified data and the context information
*/
<T> T getTarget() {
//从当前应用的ApplicationContext中得到 FeignContext 实例bean
//FeignContext 中保存了每个Feign客户端对应的ApplicationContext,在自动配置类FeignAutoConfiguration中创建(后面解析)
FeignContext context = this.applicationContext.getBean(FeignContext.class);
//得到Fegin的构建器
Feign.Builder builder = feign(context);
//从这里可见,
if (!StringUtils.hasText(this.url)) { //如果url的值为空
if (!this.name.startsWith("http")) { //如果name不是以http开头
this.url = "http://" + this.name;
}
else {
this.url = this.name;
}
//添加上path部分
this.url += cleanPath();
//得到具备负载均衡能力的Feigin客户端
return (T) loadBalance(builder, context,
new HardCodedTarget<>(this.type, this.name, this.url));
}
if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
this.url = "http://" + this.url;
}
//添加上path部分
String url = this.url + cleanPath();
//从FeginContext中得到Client类型的Bean【从名为this.contextId的ApplicationContext中得到类型为type的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
client = ((LoadBalancerFeignClient) client).getDelegate();
}
//给builder对象设值
builder.client(client);
}
//从FeginContext中得到Targeter类型的Bean
Targeter targeter = get(context, Targeter.class);
return (T) targeter.target(this, builder, context,
new HardCodedTarget<>(this.type, this.name, url));
}
protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
HardCodedTarget<T> target) {
// 从FeignContext中得到Client类型的Bean【从this.contextId指定名称的ApplicationContext中得到类型为type的Bean】。
// 该实例在FeignRibbonClientAutoConfiguration配置中引入的HttpClientFeignLoadBalancedConfiguration配置类中进行了配置
// 默认是底层使用Apache HttpClient封装的LoadBalancerFeignClient对象
// 在LoadBalancerFeignClient内部使用Ribbon的SpringClientFactory来完成某些操作,从而实现与Ribbo的集成
Client client = getOptional(context, Client.class);
if (client != null) {
//设置Client的值
builder.client(client);
//从FeignContext中得到Client类型的Bean【从this.contextId指定名称的ApplicationContext中得到类型为type的Bean】。
//HystrixTargeter、DefaultTargeter【在FeignAutoConfiguration中配置】
Targeter targeter = get(context, Targeter.class);
//返回目标对象【源码见下面】
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?");
}
protected Feign.Builder feign(FeignContext context) {
FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
Logger logger = loggerFactory.create(this.type);
// @formatter:off
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));
// @formatter:on
configureFeign(context, builder);
return builder;
}
protected void configureFeign(FeignContext context, Feign.Builder builder) {
FeignClientProperties properties = this.applicationContext
.getBean(FeignClientProperties.class);
if (properties != null) {
if (properties.isDefaultToProperties()) {
configureUsingConfiguration(context, builder);
configureUsingProperties(
properties.getConfig().get(properties.getDefaultConfig()),
builder);
configureUsingProperties(properties.getConfig().get(this.contextId),
builder);
}
else {
configureUsingProperties(
properties.getConfig().get(properties.getDefaultConfig()),
builder);
configureUsingProperties(properties.getConfig().get(this.contextId),
builder);
configureUsingConfiguration(context, builder);
}
}
else {
configureUsingConfiguration(context, builder);
}
}
protected void configureUsingConfiguration(FeignContext context,
Feign.Builder builder) {
Logger.Level level = getOptional(context, Logger.Level.class);
if (level != null) {
builder.logLevel(level);
}
Retryer retryer = getOptional(context, Retryer.class);
if (retryer != null) {
builder.retryer(retryer);
}
ErrorDecoder errorDecoder = getOptional(context, ErrorDecoder.class);
if (errorDecoder != null) {
builder.errorDecoder(errorDecoder);
}
Request.Options options = getOptional(context, Request.Options.class);
if (options != null) {
builder.options(options);
}
Map<String, RequestInterceptor> requestInterceptors = context
.getInstances(this.contextId, RequestInterceptor.class);
if (requestInterceptors != null) {
builder.requestInterceptors(requestInterceptors.values());
}
QueryMapEncoder queryMapEncoder = getOptional(context, QueryMapEncoder.class);
if (queryMapEncoder != null) {
builder.queryMapEncoder(queryMapEncoder);
}
if (this.decode404) {
builder.decode404();
}
}
protected void configureUsingProperties(
FeignClientProperties.FeignClientConfiguration config,
Feign.Builder builder) {
if (config == null) {
return;
}
if (config.getLoggerLevel() != null) {
builder.logLevel(config.getLoggerLevel());
}
if (config.getConnectTimeout() != null && config.getReadTimeout() != null) {
builder.options(new Request.Options(config.getConnectTimeout(),
config.getReadTimeout()));
}
if (config.getRetryer() != null) {
Retryer retryer = getOrInstantiate(config.getRetryer());
builder.retryer(retryer);
}
if (config.getErrorDecoder() != null) {
ErrorDecoder errorDecoder = getOrInstantiate(config.getErrorDecoder());
builder.errorDecoder(errorDecoder);
}
if (config.getRequestInterceptors() != null
&& !config.getRequestInterceptors().isEmpty()) {
// this will add request interceptor to builder, not replace existing
for (Class<RequestInterceptor> bean : config.getRequestInterceptors()) {
RequestInterceptor interceptor = getOrInstantiate(bean);
builder.requestInterceptor(interceptor);
}
}
if (config.getDecode404() != null) {
if (config.getDecode404()) {
builder.decode404();
}
}
if (Objects.nonNull(config.getEncoder())) {
builder.encoder(getOrInstantiate(config.getEncoder()));
}
if (Objects.nonNull(config.getDecoder())) {
builder.decoder(getOrInstantiate(config.getDecoder()));
}
if (Objects.nonNull(config.getContract())) {
builder.contract(getOrInstantiate(config.getContract()));
}
}
}
以上过程大致做了以下几件事
1)从this.contextId指定的ApplicationContext中获取类型为feign.Feign.Builder的实例对象(feign.Feign.Builder是个静态内部类),并为Builder对象设置Retryer、ErrorDecoder、RequestInterceptors等
2)从this.contextId指定名称的ApplicationContext中获取类型为feign.Client的实例对象,并设置给Builder【这是与Ribbon集成的关键点】
3)从this.contextId指定名称的ApplicationContext中获取类型为org.springframework.cloud.openfeign.Targeter的实例对象,并设置给Builder【关键点】
4)调用Targeter的target方法,并将结果返回【就是依赖注入的实例】
5)Targeter的target方法内部会调用feign.Feign.Builder的target方法并返回
6)在feign.Feign.Builder内部使用java动态代理创建接口实例Bean
常见的Targeter对象有下面两个
1,org.springframework.cloud.openfeign.DefaultTargeter
class DefaultTargeter implements Targeter {
@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
FeignContext context, Target.HardCodedTarget<T> target) {
return feign.target(target);
}
}
2,org.springframework.cloud.openfeign.HystrixTargeter
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);
}
feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
String name = StringUtils.isEmpty(factory.getContextId()) ? factory.getName()
: factory.getContextId();
SetterFactory setterFactory = getOptional(name, context, SetterFactory.class);
if (setterFactory != null) {
builder.setterFactory(setterFactory);
}
Class<?> fallback = factory.getFallback();
if (fallback != void.class) {
return targetWithFallback(name, context, target, builder, fallback);
}
Class<?> fallbackFactory = factory.getFallbackFactory();
if (fallbackFactory != void.class) {
return targetWithFallbackFactory(name, context, target, builder,
fallbackFactory);
}
return feign.target(target);
}
private <T> T targetWithFallbackFactory(String feignClientName, FeignContext context,
Target.HardCodedTarget<T> target, HystrixFeign.Builder builder,
Class<?> fallbackFactoryClass) {
FallbackFactory<? extends T> fallbackFactory = (FallbackFactory<? extends T>) getFromContext(
"fallbackFactory", feignClientName, context, fallbackFactoryClass,
FallbackFactory.class);
return builder.target(target, fallbackFactory);
}
private <T> T targetWithFallback(String feignClientName, FeignContext context,
Target.HardCodedTarget<T> target, HystrixFeign.Builder builder,
Class<?> fallback) {
T fallbackInstance = getFromContext("fallback", feignClientName, context,
fallback, target.type());
return builder.target(target, fallbackInstance);
}
private <T> T getFromContext(String fallbackMechanism, String feignClientName,
FeignContext context, Class<?> beanType, Class<T> targetType) {
Object fallbackInstance = context.getInstance(feignClientName, beanType);
if (fallbackInstance == null) {
throw new IllegalStateException(String.format(
"No " + fallbackMechanism
+ " instance of type %s found for feign client %s",
beanType, feignClientName));
}
if (!targetType.isAssignableFrom(beanType)) {
throw new IllegalStateException(String.format("Incompatible "
+ fallbackMechanism
+ " instance. Fallback/fallbackFactory of type %s is not assignable to %s for feign client %s",
beanType, targetType, feignClientName));
}
return (T) fallbackInstance;
}
private <T> T getOptional(String feignClientName, FeignContext context,
Class<T> beanType) {
return context.getInstance(feignClientName, beanType);
}
}
Feign.Builder类的源码
public static class Builder {
//拦截器
private final List<RequestInterceptor> requestInterceptors =
new ArrayList<RequestInterceptor>();
private Logger.Level logLevel = Logger.Level.NONE;
private Contract contract = new Contract.Default();
private Client client = new Client.Default(null, null);
private Retryer retryer = new Retryer.Default();
private Logger logger = new NoOpLogger();
private Encoder encoder = new Encoder.Default();
private Decoder decoder = new Decoder.Default();
private QueryMapEncoder queryMapEncoder = new QueryMapEncoder.Default();
private ErrorDecoder errorDecoder = new ErrorDecoder.Default();
private Options options = new Options();
private InvocationHandlerFactory invocationHandlerFactory =
new InvocationHandlerFactory.Default();
private boolean decode404;
private boolean closeAfterDecode = true;
private ExceptionPropagationPolicy propagationPolicy = NONE;
public Builder logLevel(Logger.Level logLevel) {
this.logLevel = logLevel;
return this;
}
public Builder contract(Contract contract) {
this.contract = contract;
return this;
}
public Builder client(Client client) {
this.client = client;
return this;
}
public Builder retryer(Retryer retryer) {
this.retryer = retryer;
return this;
}
public Builder logger(Logger logger) {
this.logger = logger;
return this;
}
public Builder encoder(Encoder encoder) {
this.encoder = encoder;
return this;
}
public Builder decoder(Decoder decoder) {
this.decoder = decoder;
return this;
}
public Builder queryMapEncoder(QueryMapEncoder queryMapEncoder) {
this.queryMapEncoder = queryMapEncoder;
return this;
}
public Builder mapAndDecode(ResponseMapper mapper, Decoder decoder) {
this.decoder = new ResponseMappingDecoder(mapper, decoder);
return this;
}
public Builder decode404() {
this.decode404 = true;
return this;
}
public Builder errorDecoder(ErrorDecoder errorDecoder) {
this.errorDecoder = errorDecoder;
return this;
}
public Builder options(Options options) {
this.options = options;
return this;
}
public Builder requestInterceptor(RequestInterceptor requestInterceptor) {
this.requestInterceptors.add(requestInterceptor);
return this;
}
public Builder requestInterceptors(Iterable<RequestInterceptor> requestInterceptors) {
this.requestInterceptors.clear();
for (RequestInterceptor requestInterceptor : requestInterceptors) {
this.requestInterceptors.add(requestInterceptor);
}
return this;
}
public Builder invocationHandlerFactory(InvocationHandlerFactory invocationHandlerFactory) {
this.invocationHandlerFactory = invocationHandlerFactory;
return this;
}
public Builder doNotCloseAfterDecode() {
this.closeAfterDecode = false;
return this;
}
public Builder exceptionPropagationPolicy(ExceptionPropagationPolicy propagationPolicy) {
this.propagationPolicy = propagationPolicy;
return this;
}
public <T> T target(Class<T> apiType, String url) {
return target(new HardCodedTarget<T>(apiType, url));
}
//被上面调用
public <T> T target(Target<T> target) {
return build().newInstance(target);
}
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);
//最后会构建一个ReflectiveFeign类型的对象,内部通过动态代理来处理
return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}
}
到此,其Feign实现的基本流程已经解析完成,主要就是扫描得到各个由@FeignClient注解标注的客户端,然后为每个客户端创建一个FeignClientFactoryBean工厂Bean对象,通过在该工厂Bean内部为Feign客户端创建动态代理对象来实现与Ribbon以及Hystrix的集成。
在后面的章节中将继续解析Feign的自动装配过程,即上面代码中FeignContext、Feign.Builder、Client 、Targeter 等对象的创建过程。
网友评论