首页 > 编程语言 >Feign源码解析:初始化过程(一)

Feign源码解析:初始化过程(一)

时间:2023-12-16 21:35:33浏览次数:49  
标签:初始化 Feign FeignClient bean 源码 FeignClientSpecification 注解 configuration beanDefi

前言

打算系统分析下Feign的代码,上一篇讲了下Feign的历史,本篇的话,先讲下Feign相关的beanDefinition,beanDefinition就是bean的设计图,bean都是按照beanDefinition来制造的。

Feign相关的bean不少,有一些是因为我们的Feign相关注解而引入的,有一部分是因为spring的自动装配来自动引入的。

今天讲讲因为我们注解引入的那些。

EnableFeignClients引入的FeignClientSpecification

如果项目用到Feign,在@SpringBootApplication注解的主类上,我们一般还会加上@EnableFeignClients注解。

package a.b.c;
    
@SpringBootApplication
@EnableFeignClients
public class DemoApplication

实际上,spring的beanFactory初始化一般就是分两步,第一步,收集beanDefinition列表,第二步,根据beanDefinition生成并初始化bean。

收集beanDefinition相当重要,但是beanDefinition从哪来来呢,一开始的时候,都是框架默认注册了几个,应用自身的beanDefinition一般要从主类来,也就是上面说的@SpringBootApplication注解的主类。

一开始就会去解析主类上的注解,包名;解析包名的原因是,拿到包名后,默认就会去扫描这个包名下的class,再找到注解了@configuration、@controller、@service、@component之类的bean作为beanDefinition;解析注解的原因是,可以根据注解,引入更多的beanDefinition。

以@EnableFeignClients为例,该注解的定义如下:

@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients

所以这里会通过Import引入更多的beanDefinition。

org.springframework.cloud.openfeign.FeignClientsRegistrar

public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    // 1
    registerDefaultConfiguration(metadata, registry);
    // 2
    registerFeignClients(metadata, registry);
}

上面的1处,如下:

Map<String, Object> defaultAttrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName(), true);
String name = "default." + metadata.getClassName();
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(builder.getBeanDefinition());
}

主要是获取@EnableFeignClients这个注解相关的默认属性,然后注册了一个FeignClientSpecification类型的bean。

这个FeignClientSpecification类很简单,属性就两个:

public class FeignClientSpecification implements NamedContextFactory.Specification {

	private String name;

	private Class<?>[] configuration;
}    

一个name,一个configuration,其实就是代表了一个要如何创建和配置FeignClient的配置,包含了如何创建feign的encoder、decoder等。

A custom @Configuration for all feign clients. Can contain override @Bean definition for the pieces that make up the client, for instance feign.codec.Decoder, feign.codec.Encoder, feign.Contract.

我们举个例子,如下的代码,

package a.b.c;
    
@SpringBootApplication
@EnableFeignClients
public class DemoApplication

最终生成的FeignClientSpecification beanDefinition,beanName为:default.a.b.c.DemoApplication.FeignClientSpecification,属性:

name:default.a.b.c.DemoApplication

configuration:空数组

FeignClient注解引入的FeignClientSpecification

说完这个,继续说如下的二处:

public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    // 1
    registerDefaultConfiguration(metadata, registry);
    // 2
    registerFeignClients(metadata, registry);
}

2处内部,是首先获取@EnableFeignClients注解的类所在的包名,然后在这个包名下扫描注解了@FeignClient的类,扫描到的这些类,那肯定是弄了一个beanDefinition了,这个可以倒推,毕竟每个@FeignClient注解的类最终都变成了一个bean嘛。

里面有一点值得讲,FeignClient注解中有一个属性,叫configuration,它支持你指定一个class,里面可以覆盖FeignClient内部的部分组件,如Feign的Encoder等

public @interface FeignClient {
    String value() default "";
    Class<?>[] configuration() default {};
}    

这个你指定的配置类,是可以被多个FeignClient复用的,所以,spring内部也是只会存一份,这一份配置,就通过一个创建一个类型为FeignClientSpecification.class的bean来保存。

以如下为例:

@FeignClient(name = "demoService")
public interface DemoServiceFeignClient

由于没有指定configuration属性,这里生成的FeignClientSpecification bean中,name就是demoService,configuration就是null:

public class FeignClientSpecification implements NamedContextFactory.Specification {

	private String name;

	private Class<?>[] configuration;
}

如果指定configuration属性:

@FeignClient(name = "demoService", configuration = CustomConfig.class)
public interface DemoServiceFeignClient

FeignClientSpecification中的configuration就会是CustomConfig.class。

这个FeignClientSpecification.class的bean的名字,默认则会是demoService.FeignClientSpecification。

FeignClient对应的beanDefinition

接下来,就是注册一个FeignClient对应的beanDefinition了。

详细的源码可以看这里:org.springframework.cloud.openfeign.FeignClientsRegistrar#registerFeignClient

FeignClient的bean是较难创建的,所以这里是用了工厂bean:FeignClientFactoryBean

String name = getName(attributes);

FeignClientFactoryBean factoryBean = new FeignClientFactoryBean();
factoryBean.setBeanFactory(beanFactory);
factoryBean.setName(name);
factoryBean.setContextId(contextId);
factoryBean.setType(clazz);
factoryBean.setRefreshableClient(isClientRefreshEnabled());
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(clazz, () -> {
    factoryBean.setUrl(getUrl(beanFactory, attributes));
    factoryBean.setPath(getPath(beanFactory, attributes));
    ...

    return factoryBean.getObject();
});

BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, qualifiers);
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);

这里的beanname,一般也就是FeignClient的全路径名。

汇总

最终其实就引入了三个bean:

image-20231216211043164

首先是由@enableFeignClients引入的FeignClientSpecification,然后是@enableFeignClients注解所在的包名下的由@FeignClient注解引入的FeignClientSpecification,再一个就是FeignClient本身的bean(一个factoryBean)

标签:初始化,Feign,FeignClient,bean,源码,FeignClientSpecification,注解,configuration,beanDefi
From: https://www.cnblogs.com/grey-wolf/p/17908400.html

相关文章

  • 小白也能看懂的企业内训系统源码开发:技术实践
    在当今快速发展的企业环境中,培训和发展成为了组织持续成功的关键因素。本文将介绍一个简单而强大的企业内训系统的源码开发,旨在让技术小白也能够理解和应用。 一、技术选型在开始源码开发之前,我们首先需要选择适当的技术栈。考虑到小白也能看懂的原则,我们选择了常用且易学的技术,如......
  • 端+云+大数据的智慧工地管理平台源码
    智慧工地管理平台是将互联网+的理念和技术引入建筑工地,从施工现场源头抓起,最大程度的收集人员、安全、环境、材料等关键业务数据,依托物联网、互联网,建立云端大数据管理平台,形成“端+云+大数据”的业务体系和新的管理模式,打通从一线操作与远程监管的数据链条,实现劳务、安全、环境、......
  • Kubernetes: client-go 源码剖析(二)
    上接Kubernetes:client-go源码剖析(一)2.3运行informer运行informer将Reflector,informer和indexer组件关联以实现informer流程图的流程。2.3.1ReflectorList&Watch运行informer:informer.Run(stopCh)//client-go/tools/cache/shared_informer.gofunc(s*s......
  • 直播平台源码,教你如何写出同步与异步
    直播平台源码,教你如何写出同步与异步同步示例代码:console.log("开始");functionsyncOperation(){console.log("同步操作");}syncOperation();console.log("结束"); 输出结果:开始同步操作结束 在上述代码中,同步操作syncOperation()按照顺序......
  • C#_Win32_PInvoke源码生成器
    介绍一个源代码生成器,用于向C#项目添加一组用户定义的Win32P/Invoke方法和相关的类型。链接地址:https://github.com/microsoft/CsWin32还在手动添加平台调用的代码或者增加无用的程序集?微软的官方解决方案来了!特色快速将P/Invoke方法和相关类型添加到您的C#项目......
  • KCP源码剖析和应用解析
     一,什么是KCPKCP是一个快速可靠的协议,基于UDP的类似TCP的协议。随着网络带宽的不断增大,在很多网络应用场景中,TCP的旧有特性对当今一些要求及时响应的网络要求不符合。而TCP又是嵌在操作系统内核中的模块,用户态软件不能够自定义来修改太多TCP的细节。所以推出了KCP以应对延迟越......
  • Kubernetes: client-go 源码剖析(一)
    0.前言在看kube-scheduler组件的过程中遇到了kube-scheduler对于client-go的调用,泛泛的理解调用过程总有种隔靴搔痒的感觉,于是调转头先把client-go理清楚在回来看kube-scheduler。为什么要看client-go,并且要深入到原理,源码层面去看。很简单,因为它很重要。重要在两方......
  • java智慧校园物联网平台源码
    智慧校园特征综合运用物联网、大数据、人工智能等新兴技术;构建智能感知环境,构建新式的教务课堂空間,智能识别老师学生群体的学习、工作场景和个体特性;促进教课、学习、管理、生活和文化的流程优化与体统重构;提升教育人才培养质量和教育管理决策水平;建立“可认知、可诊断、可分......
  • cas客户端流程详解(源码解析)--单点登录
    博主之前一直使用了cas客户端进行用户的单点登录操作,决定进行源码分析来看cas的整个流程,以便以后出现了问题还不知道是什么原因导致的cas主要的形式就是通过过滤器的形式来实现的,来,贴上示例配置:<listener><listener-class>org.jasig.cas.client.session.SingleSig......
  • 基于SpringBoot的网上租赁系统-计算机毕业设计源码+LW文档
    摘要本课题是根据用户的需要以及网络的优势建立的一个基于SpringBoot的网上租贸系统,来满足用户网络商品租赁的需求。本网上租贸系统应用Java技术,MYSQL数据库存储数据,基于SpringBoot框架开发。在网站的整个开发过程中,首先对系统进行了需求分析,设计出系统的主要功能模块,其次对网......