我们知道SpringCloud-OpenFeign 是通过jdk 的动态代理为接口生成代理对象,从而由代理对象完成请求的访问,下面具体看下在Spring 中 是如何完成对接口的代理的;
通常在启动类中我们需要添加 @EnableFeignClients 注解来开启feign 接口的扫描;
在 EnableFeignClients 注解中定义了一些属性可供我们进行设置:
@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 {};
}
在EnableFeignClients 类中 通过import 导入的FeignClientsRegistrar 完成了feign 接口的扫描:
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {}
FeignClientsRegistrar 实现了 ImportBeanDefinitionRegistrar 接口,意味着它可以向spring 容器中注册 bean 定义,从而在spring 创建一些bean 的时候就可以根据bean 定义来生成相应的bean;
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
// 注册 EnableFeignClients 中设置的 defaultConfiguration 注册 bean 定义
this.registerDefaultConfiguration(metadata, registry);
// 根据路径扫描 得到被 FeignClient 注解修饰的接口类,注册每个 FeignClient 中定义的 configuration
// 然后为每个FeignClient 注册bean 定义
this.registerFeignClients(metadata, registry);
}
1) EnableFeignClients 中defaultConfiguration 定义:
rivate void registerDefaultConfiguration(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
Map<String, Object> defaultAttrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName(), true);
if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
// 如果 设置了defaultConfiguration
String name;
if (metadata.hasEnclosingClass()) {
name = "default." + metadata.getEnclosingClassName();
} else {
name = "default." + metadata.getClassName();
}
// 最终生成defaultConfiguration 的bean bean 名称为
// default.被@EnableFeignClients 注解修饰类全路径.FeignClientSpecification
this.registerClientConfiguration(registry, name, defaultAttrs.get("defaultConfiguration"));
}
}
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());
}
2 ) FeignClient 接口bean 定义:
通过在 EnableFeignClients 定义的路基进行扫描 ,过滤出来被@FeignClient 修饰的类
public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet();
Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
Class<?>[] clients = attrs == null ? null : (Class[])((Class[])attrs.get("clients"));
if (clients != null && clients.length != 0) {
Class[] var12 = clients;
int var14 = clients.length;
for(int var16 = 0; var16 < var14; ++var16) {
Class<?> clazz = var12[var16];
candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));
}
} else {
// 扫描器扫描
ClassPathScanningCandidateComponentProvider scanner = this.getScanner();
scanner.setResourceLoader(this.resourceLoader);
// 扫描 被FeignClient 注解修饰的类
scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
Set<String> basePackages = this.getBasePackages(metadata);
Iterator var8 = basePackages.iterator();
// 遍历添加到 candidateComponents 集合
while(var8.hasNext()) {
String basePackage = (String)var8.next();
candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
}
}
// 遍历扫描到的类 进行bean 注册的行医
Iterator var13 = candidateComponents.iterator();
while(var13.hasNext()) {
BeanDefinition candidateComponent = (BeanDefinition)var13.next();
if (candidateComponent instanceof AnnotatedBeanDefinition) {
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 = this.getClientName(attributes);
// 注册每个FeignClient 接口定义的 configuration 配置类
this.registerClientConfiguration(registry, name, attributes.get("configuration"));
// 注册每个FeignClient
this.registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
registerClientConfiguration 注册每个FeignClient 接口定义的 configuration 配置类 :
private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object configuration) {
// BeanDefinitionBuilder 属性设置
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FeignClientSpecification.class);
builder.addConstructorArgValue(name);
builder.addConstructorArgValue(configuration);
// bean 定义注册
registry.registerBeanDefinition(name + "." + FeignClientSpecification.class.getSimpleName(), builder.getBeanDefinition());
}
FeignClient feign 接口的bean定义注册:
通过构建FeignClientFactoryBean 对象 进行注册
private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
String className = annotationMetadata.getClassName();
Class clazz = ClassUtils.resolveClassName(className, (ClassLoader)null);
ConfigurableBeanFactory beanFactory = registry instanceof ConfigurableBeanFactory ? (ConfigurableBeanFactory)registry : null;
String contextId = this.getContextId(beanFactory, attributes);
String name = this.getName(attributes);
// 构建FeignClientFactoryBean 对象并对其属性进行设置
FeignClientFactoryBean factoryBean = new FeignClientFactoryBean();
factoryBean.setBeanFactory(beanFactory);
factoryBean.setName(name);
factoryBean.setContextId(contextId);
factoryBean.setType(clazz);
factoryBean.setRefreshableClient(this.isClientRefreshEnabled());
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(clazz, () -> {
// 设置 @FeignClient 设置的url ,path ,decode404,fallback ,fallbackFactory 属性
// url 和 path 可以使用类似于 "${xxx} 的占位符,在创建bean 的过程中会对其进行解析
factoryBean.setUrl(this.getUrl(beanFactory, attributes));
factoryBean.setPath(this.getPath(beanFactory, attributes));
factoryBean.setDecode404(Boolean.parseBoolean(String.valueOf(attributes.get("decode404"))));
Object fallback = attributes.get("fallback");
if (fallback != null) {
factoryBean.setFallback(fallback instanceof Class ? (Class)fallback : ClassUtils.resolveClassName(fallback.toString(), (ClassLoader)null));
}
Object fallbackFactory = attributes.get("fallbackFactory");
if (fallbackFactory != null) {
factoryBean.setFallbackFactory(fallbackFactory instanceof Class ? (Class)fallbackFactory : ClassUtils.resolveClassName(fallbackFactory.toString(), (ClassLoader)null));
}
return factoryBean.getObject();
});
definition.setAutowireMode(2);
definition.setLazyInit(true);
this.validate(attributes);
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
beanDefinition.setAttribute("factoryBeanObjectType", className);
beanDefinition.setAttribute("feignClientsRegistrarFactoryBean", factoryBean);
boolean primary = (Boolean)attributes.get("primary");
beanDefinition.setPrimary(primary);
String[] qualifiers = this.getQualifiers(attributes);
if (ObjectUtils.isEmpty(qualifiers)) {
qualifiers = new String[]{contextId + "FeignClient"};
}
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, qualifiers);
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
this.registerOptionsBeanDefinition(registry, contextId);
}
至此就完成了 @FeignClient 客户端的扫描以及bean 的定义 ,后续spring 会依照bean 定义去创建bean,因为 FeignClientFactoryBean 实现 了 FactoryBean 所以在创建bean 的时候时,是通过调用其内部的getObject() 获取对象,然后再将bean 对象放入到单例池中;
feign 的生成主要通过FeignClientFactoryBean getObject() 进行:
FeignContext是Spring Cloud OpenFeign中的一个类,它是一个专门为Feign客户端设置的特定上下文(ApplicationContext的一个子类)。FeignContext的主要作用是允许每个Feign客户端拥有自己独立的应用上下文,这样它们就可以有自己的配置(例如编码器、解码器和拦截器),而不是使用全局的默认配置。
FeignContext 的加载是在 spring.factories FeignAutoConfiguration 类中进行的
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
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
@Bean
public FeignContext feignContext() {
FeignContext context = new FeignContext();
context.setConfigurations(this.configurations);
return context;
}
FeignClientFactoryBean 通过 getTarget() 方法 创建代理类并进行返回
public Object getObject() {
return this.getTarget();
}
<T> T getTarget() {
// 从容器中获取 FeignContext 的bean
FeignContext context = this.beanFactory != null ? (FeignContext)this.beanFactory.getBean(FeignContext.class) : (FeignContext)this.applicationContext.getBean(FeignContext.class);
// 创建组装feign 客户端的配置,这里还会为每个客户端创建子容器,保证每个客户端的独立性
Feign.Builder builder = this.feign(context);
if (!StringUtils.hasText(this.url)) {
if (LOG.isInfoEnabled()) {
LOG.info("For '" + this.name + "' URL not provided. Will try picking an instance via load-balancing.");
}
if (!this.name.startsWith("http")) {
this.url = "http://" + this.name;
} else {
this.url = this.name;
}
this.url = this.url + this.cleanPath();
return this.loadBalance(builder, context, new Target.HardCodedTarget(this.type, this.name, this.url));
} else {
if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
this.url = "http://" + this.url;
}
String url = this.url + this.cleanPath();
Client client = (Client)this.getOptional(context, Client.class);
if (client != null) {
if (client instanceof FeignBlockingLoadBalancerClient) {
client = ((FeignBlockingLoadBalancerClient)client).getDelegate();
}
if (client instanceof RetryableFeignBlockingLoadBalancerClient) {
client = ((RetryableFeignBlockingLoadBalancerClient)client).getDelegate();
}
builder.client(client);
}
this.applyBuildCustomizers(context, builder);
// 调用 target 方法完成代理对象的创建
Targeter targeter = (Targeter)this.get(context, Targeter.class);
return targeter.target(this, builder, context, new Target.HardCodedTarget(this.type, this.name, url));
}
}
此方法配置 feign 客户端的 log 日志输出,编解码,使用的协议等;
protected Feign.Builder feign(FeignContext context) {
// log 日志实现类的获取
FeignLoggerFactory loggerFactory = (FeignLoggerFactory)this.get(context, FeignLoggerFactory.class);
Logger logger = loggerFactory.create(this.type);
// 链式调用 设置 编解码 ,contract 协议
Feign.Builder builder = ((Feign.Builder)this.get(context, Feign.Builder.class)).logger(logger).encoder((Encoder)this.get(context, Encoder.class)).decoder((Decoder)this.get(context, Decoder.class)).contract((Contract)this.get(context, Contract.class));
// 设置 系统默认的配置文件,客户端自定义的配置文件
this.configureFeign(context, builder);
return builder;
}
在对 Feign.Builder 对象链式设置里,如果发现当前feign 客户端还没有创建上下文,会先进行上下文的创建:
protected <T> T get(FeignContext context, Class<T> type) {
// 从当前feign 客户端的容器中,获取到对应的 bean (Feign.Builder,Decoder,Encoder,Contract)
T instance = context.getInstance(this.contextId, type);
if (instance == null) {
throw new IllegalStateException("No bean found of type " + type + " for " + this.contextId);
} else {
return instance;
}
}
getInstance 获取当前feign 客户端容器内的bean:
public <T> T getInstance(String name, Class<T> type) {
// 根据当前feign 客户端定义 id 获取当前feign 客户端的容器,这里如果feign 客户端的容器还没创建则会先进行创建
AnnotationConfigApplicationContext context = this.getContext(name);
try {
// 在当前feign 客户端 容器内获取bean
return context.getBean(type);
} catch (NoSuchBeanDefinitionException var5) {
return null;
}
}
this.getContext 当前客户端容器获取:
protected AnnotationConfigApplicationContext getContext(String name) {
if (!this.contexts.containsKey(name)) {
// 容器内没有当前客户端的子容器
synchronized(this.contexts) {
if (!this.contexts.containsKey(name)) {
// createContext 创建子容器 并放入到spring 的大容器中
this.contexts.put(name, this.createContext(name));
}
}
}
// 返回当前客户端的子容器
return (AnnotationConfigApplicationContext)this.contexts.get(name);
}
createContext 客户端子容器的创建:
protected AnnotationConfigApplicationContext createContext(String name) {
// 创建一个 context 上下文
AnnotationConfigApplicationContext context;
if (this.parent != null) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
if (this.parent instanceof ConfigurableApplicationContext) {
beanFactory.setBeanClassLoader(((ConfigurableApplicationContext)this.parent).getBeanFactory().getBeanClassLoader());
} else {
beanFactory.setBeanClassLoader(this.parent.getClassLoader());
}
context = new AnnotationConfigApplicationContext(beanFactory);
context.setClassLoader(this.parent.getClassLoader());
} else {
context = new AnnotationConfigApplicationContext();
}
// 在当前子容器内 注册属于自己的 configuration 配置文件
if (this.configurations.containsKey(name)) {
Class[] var9 = ((Specification)this.configurations.get(name)).getConfiguration();
int var4 = var9.length;
for(int var5 = 0; var5 < var4; ++var5) {
Class<?> configuration = var9[var5];
context.register(new Class[]{configuration});
}
}
Iterator var10 = this.configurations.entrySet().iterator();
while(true) {
Map.Entry entry;
do {
if (!var10.hasNext()) {
context.register(new Class[]{PropertyPlaceholderAutoConfiguration.class, this.defaultConfigType});
context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(this.propertySourceName, Collections.singletonMap(this.propertyName, name)));
if (this.parent != null) {
// 设置spring 容器 为父容器
context.setParent(this.parent);
}
context.setDisplayName(this.generateDisplayName(name));
// 刷新子容器 去创建bean
context.refresh();
return context;
}
entry = (Map.Entry)var10.next();
} while(!((String)entry.getKey()).startsWith("default."));
Class[] var12 = ((Specification)entry.getValue()).getConfiguration();
int var13 = var12.length;
for(int var7 = 0; var7 < var13; ++var7) {
Class<?> configuration = var12[var7];
context.register(new Class[]{configuration});
}
}
}
protected void configureFeign(FeignContext context, Feign.Builder builder) {
FeignClientProperties properties = this.beanFactory != null ? (FeignClientProperties)this.beanFactory.getBean(FeignClientProperties.class) : (FeignClientProperties)this.applicationContext.getBean(FeignClientProperties.class);
FeignClientConfigurer feignClientConfigurer = (FeignClientConfigurer)this.getOptional(context, FeignClientConfigurer.class);
this.setInheritParentContext(feignClientConfigurer.inheritParentConfiguration());
if (properties != null && this.inheritParentContext) {
if (properties.isDefaultToProperties()) {
// feign 客户端定义的配置类解析
this.configureUsingConfiguration(context, builder);
// 系统默认的配置解析
this.configureUsingProperties((FeignClientProperties.FeignClientConfiguration)properties.getConfig().get(properties.getDefaultConfig()), builder);
// 配置的客户端解析
this.configureUsingProperties((FeignClientProperties.FeignClientConfiguration)properties.getConfig().get(this.contextId), builder);
} else {
this.configureUsingProperties((FeignClientProperties.FeignClientConfiguration)properties.getConfig().get(properties.getDefaultConfig()), builder);
this.configureUsingProperties((FeignClientProperties.FeignClientConfiguration)properties.getConfig().get(this.contextId), builder);
this.configureUsingConfiguration(context, builder);
}
} else {
this.configureUsingConfiguration(context, builder);
}
}
这里进行简单的说明:
configureUsingConfiguration 解析通过 @FeignClient 配置的configuration 的配置类;
configureUsingProperties:解析feign 默认的配置 比如 default 配置:
最终通过Feign 下的 Builder 类中的 target 方法生成代理对象:这里只展示部分代码
public <T> T target(Target<T> target) {
return this.build().newInstance(target);
}
public Feign build() {
super.enrich();
SynchronousMethodHandler.Factory synchronousMethodHandlerFactory = new SynchronousMethodHandler.Factory(this.client, this.retryer, this.requestInterceptors, this.responseInterceptor, this.logger, this.logLevel, this.dismiss404, this.closeAfterDecode, this.propagationPolicy, this.forceDecoding);
ReflectiveFeign.ParseHandlersByName handlersByName = new ReflectiveFeign.ParseHandlersByName(this.contract, this.options, this.encoder, this.decoder, this.queryMapEncoder, this.errorDecoder, synchronousMethodHandlerFactory);
return new ReflectiveFeign(handlersByName, this.invocationHandlerFactory, this.queryMapEncoder);
}
OpenFeign 客户端是通过 FeignClientFactoryBean 进行代理对象的生成,每个feign 客户端都会创造属于自己的子容器,并且将自身的配置文件作为bean 进行注册;我们在application.yml 进行的配置会覆盖掉我们在 @FeignClient 的配置;