1、开启FeignClient功能

我们在 app-b应用的主启动类AppBMain 上有个 @EnableFeignClients 注解,我们知道注解 @Enablexxxx 开启xxx功能,一般在这类的注解上都有@Import的注解,同样 EnableFeignClients 注解上也有。

@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {......
}

@EnableFeignClients意思就是 “开启FeignClient功能”,其本质就是在Spring容器中注册一些Bean的定义,后续根据这些定义实例化这些Bean,这些Bean实例提供了FeignClient的功能。

我们列出了简略的代码如下:

Spring容器会顺着启动类AppBMain(也是配置类),扫描到其 @Import 的类 FeignClientsRegistrar,然后使用反射对其实例化,调用栈如下

addImportBeanDefinitionRegistrar:199, ConfigurationClass (o.s.context.annotation)
// ^将FeignClientsRegistrar实例放入容器中(一个配置文件对应一个)
processImports:604, ConfigurationClassParser (o.s.context.annotation)
// ^实例化@import注解导入的类FeignClientsRegistrar(@Import(FeignClientsRegistrar.class)),并将其放入容器
// 导入的类型分三种:1、ImportSelector(会递归处理);2、ImportBeanDefinitionRegistrar;3、其他(当做配置类来处理)
doProcessConfigurationClass:304, ConfigurationClassParser (o.s.context.annotation)
processConfigurationClass:247, ConfigurationClassParser (o.s.context.annotation)
// ^处理解析出来的的配置类
parse:200, ConfigurationClassParser (o.s.context.annotation)
// ^为每个配置类创建一个ConfigurationClass实例
// 并放入ConfigurationClassParser实例的Map<ConfigurationClass, ConfigurationClass>类型的属性中
parse:169, ConfigurationClassParser (o.s.context.annotation)
// ^解析配置类
processConfigBeanDefinitions:308, ConfigurationClassPostProcessor (o.s.context.annotation)
// ^找出配置类
postProcessBeanDefinitionRegistry:228, ConfigurationClassPostProcessor (o.s.context.annotation)
invokeBeanDefinitionRegistryPostProcessors:272, PostProcessorRegistrationDelegate (o.s.context.support)
invokeBeanFactoryPostProcessors:92, PostProcessorRegistrationDelegate (o.s.context.support)
invokeBeanFactoryPostProcessors:687, AbstractApplicationContext (o.s.context.support)
refresh:525, AbstractApplicationContext (o.s.context.support)
refresh:122, EmbeddedWebApplicationContext (o.s.boot.context.embedded)
refresh:693, SpringApplication (o.s.boot)
refreshContext:360, SpringApplication (o.s.boot)
run:303, SpringApplication (o.s.boot)
run:1118, SpringApplication (o.s.boot)
run:1107, SpringApplication (o.s.boot)
main:15, AppBMain (com.yh.stu)

从调用栈中processConfigBeanDefinitions:308看出,调用栈中 processImports:604,ConfigurationClassParser ,就在这个方法中,实例化所有@import的类

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)throws IOException {......processImports(configClass, sourceClass, getImports(sourceClass), true);......
}
private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {Set<SourceClass> imports = new LinkedHashSet<SourceClass>();Set<SourceClass> visited = new LinkedHashSet<SourceClass>();collectImports(sourceClass, imports, visited);return imports;
}
// 递归调用来查找@import注解标注的 SourceClass
private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)throws IOException {if (visited.add(sourceClass)) {for (SourceClass annotation : sourceClass.getAnnotations()) {String annName = annotation.getMetadata().getClassName();if (!annName.startsWith("java") && !annName.equals(Import.class.getName())) {//递归调用collectImports(annotation, imports, visited);}}imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));}
}
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,Collection<SourceClass> importCandidates, boolean checkForCircularImports) {......for (SourceClass candidate : importCandidates) {if (candidate.isAssignable(ImportSelector.class)) {......                   }else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {// Candidate class is an ImportBeanDefinitionRegistrar ->// delegate to it to register additional bean definitionsClass<?> candidateClass = candidate.loadClass();ImportBeanDefinitionRegistrar registrar =BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);ParserStrategyUtils.invokeAwareMethods(registrar, this.environment, this.resourceLoader, this.registry);configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());}else {......}}......
}

调用其实现ImportBeanDefinitionRegistrar接口的registerBeanDefinitions方法,在这个方法中注册Feign功能需要的bean的定义到容器中。

Spring Boot的启动类 AppBMain 为什么是配置类? 因为其标注的@SpringBootApplication 间接标注了 @Configuration,而 Spring是循环递归扫描的注解的,所以Spring 认为 AppBMain 也是个配置类。

下面是我debug时候的调用栈,大家看看注释里的解释

registerFeignClient:176, FeignClientsRegistrar (o.s.cloud.netflix.feign)
registerFeignClients:167, FeignClientsRegistrar (o.s.cloud.netflix.feign)
registerBeanDefinitions:92, FeignClientsRegistrar (o.s.cloud.netflix.feign)
// 调用ImportBeanDefinitionRegistrar的registerBeanDefinitions方法,这里通常就是用来注册bean的定义的
loadBeanDefinitionsFromRegistrars:360, ConfigurationClassBeanDefinitionReader (o.s.context.annotation)
// 从容器中 ImportBeanDefinitionRegistrar实例中加载bean定义
loadBeanDefinitionsForConfigurationClass:144, ConfigurationClassBeanDefinitionReader (o.s.context.annotation)
// ^加载配置文件中的bean定义,注:可以将ImportBeanDefinitionRegistrar的实例看做bean的定义,实例里本来就是用来定义了一些bean
loadBeanDefinitions:116, ConfigurationClassBeanDefinitionReader (o.s.context.annotation)
// 使用ConfigurationClassBeanDefinitionReader 来解析配置并注册bean定义
processConfigBeanDefinitions:320, ConfigurationClassPostProcessor (o.s.context.annotation)
postProcessBeanDefinitionRegistry:228, ConfigurationClassPostProcessor (o.s.context.annotation)
// ConfigurationClassPostProcessor 循环递归解析配置并注册bean定义
invokeBeanDefinitionRegistryPostProcessors:272, PostProcessorRegistrationDelegate (o.s.context.support)
// 调用容器中的所有的BeanDefinitionRegistryPostProcessor
invokeBeanFactoryPostProcessors:92, PostProcessorRegistrationDelegate (o.s.context.support)
invokeBeanFactoryPostProcessors:687, AbstractApplicationContext (o.s.context.support)
refresh:525, AbstractApplicationContext (o.s.context.support)
refresh:122, EmbeddedWebApplicationContext (o.s.boot.context.embedded)
refresh:693, SpringApplication (o.s.boot)
refreshContext:360, SpringApplication (o.s.boot)
run:303, SpringApplication (o.s.boot)
run:1118, SpringApplication (o.s.boot)
run:1107, SpringApplication (o.s.boot)
main:15, AppBMain (com.yh.stu)

2、FeignClientsRegistrar注册FeignClientFactoryBean

FeignClientsRegistrar类实现了一些接口(代码如下),这些接口的作用属于Spring Framework的知识。

class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {.......
}

ℹ️​ Spring Framwork 知识- ImportBeanDefinitionRegistrar 接口
在容器启动过程的中,会调用后置处理器阶段会调用容器中所有类型为 ImportBeanDefinitionRegistrar 类型的实例的 registerBeanDefinitions(...) 方法,该方法向容器中注册Bean 的定义,这些Bean的定义在Bean的实例化时需要使用。(解析出配置的所有配置类中配置的Bean的定义,包括@Import标注的类中定义的Bean)。
在refresh的 invokeBeanFactoryPostProcessors(beanFactory) 方法中,会调用“配置类后置处理器”(ConfigurationClassPostProcessor)来解析Spring应用的配置。这个“配置类后置处理器”是Spring Framework 容器创建的阶段就注册进容器了(不是refresh阶段)。

ImportBeanDefinitionRegistrar 类型的Bean就是在这个阶段实例化并调用的。

 @Overridepublic void registerBeanDefinitions(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {registerDefaultConfiguration(metadata, registry);registerFeignClients(metadata, registry);}public void registerFeignClients(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {ClassPathScanningCandidateComponentProvider scanner = getScanner();scanner.setResourceLoader(this.resourceLoader);Set<String> basePackages;// metadata就是主启动类上的注解元数据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) {scanner.addIncludeFilter(annotationTypeFilter);//添加FeignClient注解的过滤basePackages = getBasePackages(metadata);}else {......}for (String basePackage : basePackages) {//查找包下所有标注@FeignClient的类Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);for (BeanDefinition candidateComponent : candidateComponents) {if (candidateComponent instanceof AnnotatedBeanDefinition) {// verify annotated class is an interfaceAnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();.......registerFeignClient(registry, annotationMetadata, attributes);}}}}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("name", name);// app-adefinition.addPropertyValue("type", className);.......BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,new String[] { alias });BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);}......

3、调用FeignClientFactoryBean的getObject

FeignClientsRegistrar注册了FeignClientFactoryBean的BD后,

@Override
public Object getObject() throws Exception {FeignContext context = applicationContext.getBean(FeignContext.class);Feign.Builder builder = feign(context);if (!StringUtils.hasText(this.url)) {String url;if (!this.name.startsWith("http")) {url = "http://" + this.name;}else {url = this.name;}url += cleanPath();return loadBalance(builder, context, new HardCodedTarget<>(this.type,this.name, url));}if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {this.url = "http://" + this.url;}// 如果你在@FeignClient上,没有配置url属性,也就是你没有自己指定服务的url地址,那么就会自动跟ribbon关联起来,// 采用ribbon来进行负载均衡,直接就开始为ribbon来准备对应的url地址了String url = this.url + cleanPath();Client client = getOptional(context, Client.class);if (client != null) {if (client instanceof LoadBalancerFeignClient) {// not lod balancing because we have a url,// but ribbon is on the classpath, so unwrapclient = ((LoadBalancerFeignClient)client).getDelegate();}builder.client(client);}Targeter targeter = get(context, Targeter.class);return targeter.target(this, builder, context, new HardCodedTarget<>(this.type, this.name, url));
}
protected Feign.Builder feign(FeignContext context) {// 这里为app-a 创建了一个ApplicationContextFeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);Logger logger = loggerFactory.create(this.type);// @formatter:offFeign.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:onconfigureFeign(context, builder);return builder;
}protected <T> T loadBalance(Feign.Builder builder, FeignContext context,HardCodedTarget<T> target) {Client client = getOptional(context, Client.class);if (client != null) {builder.client(client);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?");
}

1、applicationContext.getBean(FeignContext.class) 调用栈:

<init>:31, FeignContext (o.s.cloud.netflix.feign)
feignContext:72, FeignAutoConfiguration (o.s.cloud.netflix.feign)
CGLIB$feignContext$1:-1, FeignAutoConfiguration$$EnhancerBySpringCGLIB$$c4e49129
(o.s.cloud.netflix.feign)
invoke:-1, FeignAutoConfiguration$$EnhancerBySpringCGLIB$$c4e49129
$$FastClassBySpringCGLIB$$55cd8dc9 (o.s.cloud.netflix.feign)invokeSuper:228, MethodProxy (o.s.cglib.proxy)
intercept:358, ConfigurationClassEnhancer$BeanMethodInterceptor (o.s.context.annotation)
feignContext:-1, FeignAutoConfiguration$$EnhancerBySpringCGLIB$$c4e49129 (o.s.cloud.netflix.feign)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
instantiate:162, SimpleInstantiationStrategy (o.s.beans.factory.support)
// ^ 反射调用return factoryMethod.invoke(factoryBean, args);这里的factoryBean是CGLIB代理类:
// FeignAutoConfiguration$$EnhancerBySpringCGLIB$$c4e49129
instantiateUsingFactoryMethod:588, ConstructorResolver (o.s.beans.factory.support)
instantiateUsingFactoryMethod:1181, AbstractAutowireCapableBeanFactory (o.s.beans.factory.support)
createBeanInstance:1075, AbstractAutowireCapableBeanFactory (o.s.beans.factory.support)
doCreateBean:513, AbstractAutowireCapableBeanFactory (o.s.beans.factory.support)
createBean:483, AbstractAutowireCapableBeanFactory (o.s.beans.factory.support)
getObject:312, AbstractBeanFactory$1 (o.s.beans.factory.support)
getSingleton:230, DefaultSingletonBeanRegistry (o.s.beans.factory.support)
doGetBean:308, AbstractBeanFactory (o.s.beans.factory.support)
getBean:220, AbstractBeanFactory (o.s.beans.factory.support)
resolveNamedBean:1018, DefaultListableBeanFactory (o.s.beans.factory.support)
getBean:345, DefaultListableBeanFactory (o.s.beans.factory.support)
getBean:340, DefaultListableBeanFactory (o.s.beans.factory.support)
getBean:1092, AbstractApplicationContext (o.s.context.support)
getObject:220, FeignClientFactoryBean (o.s.cloud.netflix.feign)
----------------------......

2、根据@FeignClient生成代理类相关推荐

  1. JDK和cglib生成代理类

    关于动态代理和静态代理 当一个对象(客户端)不能或者不想直接引用另一个对象(目标对象),这时可以应用代理模式在这两者之间构建一个桥梁–代理对象. 按照代理对象的创建时期不同,可以分为两种: 静态代理: ...

  2. 动态代理最全详解系列[2]-Proxy生成代理类对象源码分析

      之前我们通过JDK中的Proxy实现了动态代理,Proxy用起来是比较简便的,但理解起来不是那么清晰,是因为我们并没有看见代理类是怎么生成的,代理类怎么调用的被代理类方法,所以下面我们进入源码看一 ...

  3. 在net.tcp模式下,由SvcUtil.exe生成代理类文件和配置文件(转)

    WCF服务调用可以采用两个方法,由工具SvcUtil.exe生成本地代理服务类和配置文件方式,或者采用ChannelFactory直接创建服务代理对象.本文主要采用前面一种方式来进行. SvcUtil ...

  4. 怎么通过WSDL命令,将WSDL生成代理类

    项目场景: 调用webservice接口时,想要把webservice变成代理类,那么他的好处是什么呢?其实好处还挺多,就是如果我们我们不把它变成代理类时,每次修改了webservice的地址的话,还 ...

  5. Web Serveice服务代理类生成及编译

    一.生成代理类 对于web service服务和wcf的webservice服务,我们都可以通过一个代理类来调用. 怎么写那个代理类呢?通过一个工具生成即可!!微软为我们提供了一个wsdl.exe的W ...

  6. spring service ,controller反向代理生成AOP代理类流程

    一.在applicationContext的beanFactory.preInstantiateSingletons方法中,会初始化所有的单例BEAN. 二. 1.AbstractAutowireCa ...

  7. 填坑之动态代理生成的代理类文件在哪?

    初衷 最近想研究研究Mybatis源码,了解一下Mybatis的执行过程,从mybatis-config.xml配置文件开始,一直到Mybatis执行CRUD位置,中途出现很多疑惑,今天特记录一个! ...

  8. 查看动态代理生成的类文件

    JDK的动态代理 场景 在数据保存前后加入日志 实例 先定义数据保存接口 interface DataSave{void save(); } 然后定义一个sql拦截器,在数据保存前后打印日志 clas ...

  9. Android开发如何理解Java静态代理 动态代理及动态生成代理对象原理 看这篇就够了

    动态代理与静态代理 前言 代理模式 静态代理 动态代理 JDK代理 动态生成代理对象原理 生成class数据源码 动态代理类真身 总结 前言 近期在研究Hook技术,需要用到动态代理,说到动态代理就会 ...

最新文章

  1. R语言使用ggplot2包使用geom_boxplot函数绘制基础分组箱图(输出多个分组、色彩配置、添加数据点)实战
  2. C++实现归并排序(附完整源码)
  3. java I O类大全_Java I/O最简单的几个类
  4. python识别虚假新闻的分类器_使用NLP检测和对抗AI生成的假新闻
  5. 面试精讲之面试考点及大厂真题 - 分布式专栏 14 全面了解Kafka的使用与特性
  6. ZMQ模式详解——发布/订阅模式
  7. (转)为什么用ls和du显示出来的文件大小有差别?
  8. cognos oracle sql,Cognos联接Oracle数据库
  9. 局域网打印机共享怎么设置_局域网如何共享打印机
  10. 新浪短链接 新浪t.cn短链接在线生成生成工具
  11. Carson带你学Android:请收好这一份全面详细的Android学习指南
  12. python数据分析-面试题
  13. 环回接口---loopback
  14. 关于MySQL的版本
  15. 【持续更新】Jetson Nano 人工智能机器人开发实战案例——RosmasterX3A1
  16. 什么是扩散模型(Diffusion Models),为什么它们是图像生成的一大进步?
  17. Generative Adversarial Networks简介
  18. Vue.js基础入门到实战视频学习教程
  19. Visio中嵌入word对象时公式乱码
  20. 机器学习建模基本过程

热门文章

  1. [Ext JS 4] 布局之实战二 - 中间区块不会自动伸展 (tab)续
  2. 服务器系统的安装方法,服务器的操作系统安装方法
  3. Java Elasticsearch 使用
  4. 6种不同画法画平行线_眉毛影响气质!6种经典眉形画法,每一种都让你美丽翻倍...
  5. Python中判断字符串中是否包含另一个字符串
  6. linux+cd英文全称,Linux命令英文全称
  7. thymeleaf引用html_SpringBoot+Thymeleaf实现html文件引入(类似include功能)_html/css_WEB-ITnose...
  8. 非经营性网站备案流程
  9. sysbench压测时遇到max_prepared_stmt_count相关错误
  10. oracle ogg checkpoint,OGG 11g Checkpoint 详解