日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

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

發布時間:2023/12/24 windows 25 coder
生活随笔 收集整理的這篇文章主要介紹了 Feign源码解析:初始化过程(一) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

打算系統分析下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:

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

總結

以上是生活随笔為你收集整理的Feign源码解析:初始化过程(一)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。