目錄
@EnableFeignClients" >@EnableFeignClients
@Import()" >@Import()
openfeign中作用" >openfeign中作用
registerDefaultConfiguration(metadata, registry)" >registerDefaultConfiguration(metadata, registry)
registerFeignClients(metadata, registry)" >registerFeignClients(metadata, registry)
首頁 Java java教程 Spring Cloud原始碼分析:第一篇

Spring Cloud原始碼分析:第一篇

Aug 15, 2023 pm 04:04 PM
spring cloud

莫要嘲笑看原始碼的朋友,現在面試只是八股文是靠不住了,更多是問專案問題以及原始碼及問題。我也是逼不得已,不然誰想造輪子,很累人又枯燥!

個人覺得看原始碼的前提是得會用,用熟了可以猜猜別人是怎麼實現的,如果有相關官方文件那就在看看官方文件。

不過,可惜的是很多官方文件寫得很爛,讓你看了會那種雲裡霧裡的。

最近我在研究openfeign原始碼的時候,發現在原始碼中有個關鍵註解:@Import

專案啟動類別:

/**
 * @author tianwc  公众号:java后端技术全栈、面试专栏
 * @version 1.0.0
 * @date 2023年07月07日 16:47
 * 在线刷题 1200+题和1000+篇干货文章:<a href="http://woaijava.cc/">博客地址</a>
 */
@EnableFeignClients(basePackages = {"com.tian.feign"})
@SpringBootApplication()
public class    MqApplication {
    public static void main(String[] args) {
        SpringApplication.run(MqApplication.class, args);
    }
}
登入後複製

然後,就是我們的feignclient介面:

/**
 * @author tianwc  公众号:java后端技术全栈、面试专栏
 * @version 1.0.0
 * @date 2023年07月07日 16:47
 * 在线刷题 1200+题和1000+篇干货文章:<a href="http://woaijava.cc/">博客地址</a>
 */
@FeignClient(contextId = "userFeignClient", value = "charge-user-service")
public interface UserFeignClient {

    /**
     * 邀请成功增加收益
     *
     * @param invitedDto 邀请增加收益
     * @return 邀请成功
     */
    @PostMapping("/user/invited/register")
    CommonResult<Boolean> invitedRegister(@RequestBody InvitedDto invitedDto);
}
登入後複製

使用案例:

/**
 * @author tianwc  公众号:java后端技术全栈、面试专栏
 * @version 1.0.0
 * @date 2023年07月07日 16:47
 * 在线刷题 1200+题和1000+篇干货文章:<a href="http://woaijava.cc/">博客地址</a>
 */
@RestController
@RequestMapping("/user")
public class UserController {

    @Resource
    UserFeignClient userFeignClient;

    @PostMapping("/invited")
    public CommonResult invitedRegister(){
        //省略不想搞的代码
        return userFeignClient.invitedRegister(invitedDto);
    }

}
登入後複製

從上面在的程式碼中,我們可以看出openfeign關鍵程式碼有:

@EnableFeignClients(basePackages = {"com.tian.feign"})

@FeignClient(contextId = "userFeignClient", value = "charge-user-service")

userFeignClient.invitedRegister(invitedDto);

################################################################################################################################。 ##

@EnableFeignClients

@EnableFeignClients这个注解在启动类上,我们肯定要重点关注。

小技巧:凡是以@Enable开头的各种注解基本上都是开启xxxx。比如:@EnableFeignClients表示开启feign客户端。

我们进入@EnableFeignClients

/**
 * @author tianwc  公众号:java后端技术全栈、面试专栏
 * @version 1.0.0
 * @date 2023年07月07日 16:47
 * 在线刷题 1200+题和1000+篇干货文章:<a href="http://woaijava.cc/">博客地址</a>
 */
@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 {};
}
登入後複製

我们通常只需要关心属性basePackages,表示我们需要扫描的包目录。

如果既没有指定basePackages,也没有指定basePackageClasses,则采用启动类所在的目录作为包扫描路径。默认是这种情况。

本文重点来了,在这个注解@EnableFeignClients上有个注解@Import(FeignClientsRegistrar.class),这里到底是有什么作用?

@Import()

#@Import()註解是在spring 3.0版本中引入的,字面意義就是導入.

@Import註解的全類名是org.springframework.context.annotation.Import。其只有一個預設的value屬性,該屬性類型為Class<?>[],表示可以傳入一個或多個Class物件。

透過註解可以看出,該註解有如下作用:

可以導入一個或多個元件類別(通常是@Configuration配置類別)該註解的功能與Spring XML中的<import/>元素相同。可以導入@Configuration配置類別、ImportSelectImportBeanDefinitionRegistrar的實作類別。

從spring 4.2版本開始,也可以引用常規元件類別(普通類別),該功能類似於AnnotationConfigApplicationContext.register()方法。

此註解可以在類別中聲明,也可以在元註解中聲明。如果需要匯入XML或其他非@Configuration定義的資源,可以使用@ImportResource註解。

通常有三種使用方式:

  • @Import一个普通类 spring会将该类加载到spring容器中
  • @Import一个类,该类实现了ImportBeanDefinitionRegistrar接口,在重写的registerBeanDefinitions方法里面,能拿到BeanDefinitionRegistry的注册器,能手工往beanDefinitionMap中注册 beanDefinition
  • @Import一个类 该类实现了ImportSelector 重写selectImports方法该方法返回了String[]数组的对象,数组里面的类都会注入到spring容器当中。

下面我们来聊聊@Import在openfeign的这里是起到什么作用。

openfeign中作用

回答上面的代码里

@Import(FeignClientsRegistrar.class)
登入後複製

这里导入的是FeignClientsRegistrar类,我们再来看看他的类关系图:

Spring Cloud原始碼分析:第一篇

从类关系图来看,FeignClientsRegistrar实现了ImportBeanDefinitionRegistrar接口。再结合@Import的三种使用方式中的第二种方式,能手工往beanDefinitionMap中注册 beanDefinition

/**
 * @author tianwc  公众号:java后端技术全栈、面试专栏
 * @version 1.0.0
 * @date 2023年07月07日 16:47
 * 在线刷题 1200+题和1000+篇干货文章:<a href="http://woaijava.cc/">博客地址</a>
 */
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
                                    BeanDefinitionRegistry registry) {
    registerDefaultConfiguration(metadata, registry);
    registerFeignClients(metadata, registry);
}
登入後複製

这个方法registerBeanDefinitions()是feign的核心入口方法,其中会做两件事:

注册默认的配置和注册所有的FeignClient。

registerDefaultConfiguration(metadata, registry)

这个方法是负责注册OpenFeign的默认配置 ,逻辑相对简单:

private void registerDefaultConfiguration(AnnotationMetadata metadata,
                                          BeanDefinitionRegistry registry) {
    //获取@EnableFeignClients的全部属性
    //@EnableFeignClients(basePackages = {"com.tian.feign"}) 
    //这里的basePackages就是我们指定的熟悉
    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();
        }
        registerClientConfiguration(registry, name,
                                    defaultAttrs.get("defaultConfiguration"));
    }
}
登入後複製

defaultAttrs中内容如下:

Spring Cloud原始碼分析:第一篇

但是这里我们只关注defaultConfiguration,我们并有对其进行设置,所以我们可以忽略他。重点是下面这个方法。

registerFeignClients(metadata, registry)

这里就是项目启动时和openfeign相关的核心代码,这也是@EnableFeignClients@FeignClient两个注解关联起来的地方。

我们进入源码中:

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());
    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);
        basePackages = getBasePackages(metadata);
    }
    else {
        final Set<String> clientClasses = new HashSet<>();
        basePackages = new HashSet<>();
        for (Class<?> clazz : clients) {
            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);
                registerClientConfiguration(registry, name,
                                            attributes.get("configuration"));

                registerFeignClient(registry, annotationMetadata, attributes);
            }
        }
    }
}
登入後複製

代码一行一行看是不是觉得很累,我给你总结好了。

上面的方法分为以下七个步骤:

  • 首先取得@EnableFeignClients註解的所有屬性,主要為了拿到掃描套件路徑(basePackages);
  • #因為一般不會在@EnableFeignClients註解中配置clients屬性,所以會進入到clients屬性為空時的邏輯;
  • ##然後透過
    getScanner()方法獲取掃描器:ClassPathScanningCandidateComponentProvider,並將上下文AnnotationConfigServletWebServerApplicationContext作為掃描器的ResourceLoader
  • 接著給掃描器
    ClassPathScanningCandidateComponentProvider新增一個註解過濾器(AnnotationTypeFilter),只過濾出包含@FeignClient註解的BeanDefinition
  • #;
    ##再透過getBasePackages(metadata)
    方法取得
  • @EnableFeingClients###註解中的指定的套件掃描路徑或掃描類別;如果沒有取得到,則預設掃描啟動類別所在的套件路徑;######
  • 然後進入到核心邏輯:透過scanner.findCandidateComponents(basePackage)方法從套件路徑下掃描出所有標註了@FeignClient註解並符合條件組裝的介面;
  • 最後將FeignClientConfigurationBeanDefinitionRegistry中註冊一下,再對FeignClient做真正的註冊操作。

總結

openfeign原始碼中的@Import註解在這裡的作用就是將掃描到帶有FeignClient註解的全部介面類別以bean的形式註冊到spring IOC容器中。

再來強調@Import註解使用方式:

  • @Import一個普通類別spring會將該類別載入到spring容器中
  • @Import一個類,該類別實作了ImportBeanDefinitionRegistrar接口,在重寫的registerBeanDefinitions方法裡面,能拿到BeanDefinitionRegistry的註冊器,可以手工往 beanDefinitionMap中註冊beanDefinition
  • #@Import一個類別該類別實作了ImportSelector 重寫selectImports方法此方法傳回了String[]陣列的對象,陣列裡面的類別都會注入到spring容器當中。

好了,今天就分享這麼多。這個openfeign裡面還有很多很有意思的地方,我們下次再分享吧!

以上是Spring Cloud原始碼分析:第一篇的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
4 週前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
4 週前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
4 週前 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解鎖Myrise中的所有內容
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

巧用Spring Cloud解決微服務架構下的負載平衡問題 巧用Spring Cloud解決微服務架構下的負載平衡問題 Jun 23, 2023 pm 01:40 PM

隨著企業應用的複雜度不斷增加,越來越多的企業開始將應用程式拆分為多個微服務,透過微服務之間的協作完成整個業務流程。這種架構方式能夠使應用程式更加穩定、擴展性更強,但同時也帶來了一些新的問題,例如負載平衡,服務發現等。本文將介紹如何透過SpringCloud來解決微服務架構下的負載平衡問題。什麼是負載平衡?負載平衡(LoadBalancing)是指在多個伺服器、網

Spring Cloud原始碼分析:第一篇 Spring Cloud原始碼分析:第一篇 Aug 15, 2023 pm 04:04 PM

個人覺得看原始碼的前提是得會用,用熟了可以猜猜別人是怎麼實現的,如果有相關官方文件那就在看看官方文件。

面向服務的Spring Cloud微服務開發 面向服務的Spring Cloud微服務開發 Jun 23, 2023 pm 12:30 PM

隨著網路的發展和科技的不斷更新,傳統的單體應用已經無法滿足使用者需求,微服務的概念應運而生。 SpringCloud是一個由Pivotal公司推出的微服務開發工具包,它為開發人員提供了一種極為方便的方式來建置、部署和管理微服務架構應用程式。本文將對服務導向的SpringCloud微服務開發進行詳細介紹,包括SpringCloud的概念與架構、微服務開發流程以

Spring Cloud微服務架構部署與維 Spring Cloud微服務架構部署與維 Jun 23, 2023 am 08:19 AM

隨著網路的快速發展,企業級應用的複雜度日益增加。針對這種情況,微服務架構應運而生。它以模組化、獨立部署、可擴展性高等特點,成為當今企業級應用開發的首選。作為一種優秀的微服務架構,SpringCloud在實際應用中展現了極大的優勢。本文將介紹SpringCloud微服務架構的部署與維運。一、部署SpringCloud微服務架構SpringCloud

如何使用Java開發一個基於Spring Cloud Kubernetes的容器編排應用 如何使用Java開發一個基於Spring Cloud Kubernetes的容器編排應用 Sep 20, 2023 am 11:15 AM

如何使用Java開發一個基於SpringCloudKubernetes的容器編排應用隨著容器技術的發展和廣泛應用,容器編排工具也成為了開發人員不可或缺的一部分。 Kubernetes作為目前最受歡迎的容器編排工具之一,已經成為了業界的標準。在此背景下,結合SpringCloud和Kubernetes,我們可以很方便地開發出基於容器編排的應用程式。本文將詳細介紹

Java語言中的Spring Cloud框架介紹 Java語言中的Spring Cloud框架介紹 Jun 09, 2023 pm 10:54 PM

Java語言中的SpringCloud框架介紹隨著雲端運算和微服務的流行,SpringCloud框架成為了Java語言中建構雲端原生應用的首選框架之一。本文將介紹SpringCloud框架的概念與特點,以及如何使用SpringCloud建構微服務架構。 SpringCloud簡介SpringCloud框架是基於SpringBoot的微服務框架。它為

實現分散式鎖的Spring Cloud微服務實踐 實現分散式鎖的Spring Cloud微服務實踐 Jun 22, 2023 pm 11:28 PM

隨著微服務架構的流行,越來越多的企業開發團隊開始使用SpringCloud建立自己的微服務系統。在分散式環境下,實現分散式鎖是一項重要的技術挑戰。本文將介紹在SpringCloud框架下,如何實現分散式鎖定的微服務實踐。首先,我們要了解什麼是分散式鎖。分散式鎖是一種用於保護共享資源的存取的技術,它可以保證在分散式環境下多個節點不會同時對相同資源進行修改或

Spring Cloud微服務與元件化的結合 Spring Cloud微服務與元件化的結合 Jun 23, 2023 am 10:21 AM

隨著網路技術的不斷發展,越來越多的企業開始採用微服務架構來建構其係統。而SpringCloud則是在這背景下快速崛起的微服務框架。在此基礎上,這篇文章將對SpringCloud微服務和組件化的結合進行探討,分析其優點和實現方式。一、SpringCloud微服務的簡介SpringCloud是SpringBoot專案的升級版,它提供了大量的工

See all articles