莫要嘲笑看原始碼的朋友,現(xiàn)在面試只是八股文是靠不住了,更多是問專案問題以及原始碼及問題。我也是逼不得已,不然誰想造輪子,很累人又枯燥!
個人覺得看原始碼的前提是得會用,用熟了可以猜猜別人是怎麼實現(xiàn)的,如果有相關(guān)官方文件那就在看看官方文件。
不過,可惜的是很多官方文件寫得很爛,讓你看了會那種雲(yún)裡霧裡的。
最近我在研究openfeign
原始碼的時候,發(fā)現(xiàn)在原始碼中有個關(guān)鍵註解:@Import
。
專案啟動類別:
/** * @author tianwc 公眾號:java后端技術(shù)全棧、面試專欄 * @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后端技術(shù)全棧、面試專欄 * @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后端技術(shù)全棧、面試專欄 * @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
關(guān)鍵程式碼有:
@EnableFeignClients(basePackages = {"com.tian.feign"})
@FeignClient(contextId = "userFeignClient", value = "charge-user-service")
################################################################################################################################。 ##
userFeignClient.invitedRegister(invitedDto);
@EnableFeignClients
@EnableFeignClients
這個注解在啟動類上,我們肯定要重點關(guān)注。
小技巧:凡是以
@Enable
開頭的各種注解基本上都是開啟xxxx
。比如:@EnableFeignClients
表示開啟feign客戶端。
我們進入@EnableFeignClients
中
/** * @author tianwc 公眾號:java后端技術(shù)全棧、面試專欄 * @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 {}; }
我們通常只需要關(guān)心屬性basePackages
,表示我們需要掃描的包目錄。
如果既沒有指定
basePackages
,也沒有指定basePackageClasses
,則采用啟動類所在的目錄作為包掃描路徑。默認是這種情況。
本文重點來了,在這個注解@EnableFeignClients
上有個注解@Import(FeignClientsRegistrar.class)
,這里到底是有什么作用?
@Import()
#@Import()
註解是在spring 3.0版本中引入的,字面意義就是導入.
@Import
註解的全類名是org.springframework.context.annotation.Import
。其只有一個預設(shè)的value屬性,該屬性類型為Class<?>[]
,表示可以傳入一個或多個Class物件。
透過註解可以看出,該註解有如下作用:
可以導入一個或多個元件類別(通常是@Configuration配置類別)該註解的功能與Spring XML中的<import/>
元素相同??梢詫?code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">@Configuration配置類別、ImportSelect
和ImportBeanDefinitionRegistrar
的實作類別。
從spring 4.2版本開始,也可以引用常規(guī)元件類別(普通類別),該功能類似於AnnotationConfigApplicationContext.register()
方法。
此註解可以在類別中聲明,也可以在元註解中聲明。如果需要匯入XML或其他非@Configuration
定義的資源,可以使用@ImportResource
註解。
通常有三種使用方式:
@Import
一個普通類 spring會將該類加載到spring容器中@Import
一個類,該類實現(xiàn)了ImportBeanDefinitionRegistrar
接口,在重寫的registerBeanDefinitions
方法里面,能拿到BeanDefinitionRegistry
的注冊器,能手工往beanDefinitionMap
中注冊beanDefinition
@Import
一個類 該類實現(xiàn)了ImportSelector
重寫selectImports
方法該方法返回了String[]數(shù)組的對象,數(shù)組里面的類都會注入到spring容器當中。
下面我們來聊聊@Import
在openfeign的這里是起到什么作用。
openfeign
中作用
回答上面的代碼里
@Import(FeignClientsRegistrar.class)
這里導入的是FeignClientsRegistrar
類,我們再來看看他的類關(guān)系圖:
從類關(guān)系圖來看,FeignClientsRegistrar
實現(xiàn)了ImportBeanDefinitionRegistrar
接口。再結(jié)合@Import
的三種使用方式中的第二種方式,能手工往beanDefinitionMap
中注冊 beanDefinition
。
/** * @author tianwc 公眾號:java后端技術(shù)全棧、面試專欄 * @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
中內(nèi)容如下:
但是這里我們只關(guān)注defaultConfiguration,我們并有對其進行設(shè)置,所以我們可以忽略他。重點是下面這個方法。
registerFeignClients(metadata, registry)
這里就是項目啟動時和openfeign相關(guān)的核心代碼,這也是@EnableFeignClients
和@FeignClient
兩個注解關(guān)聯(lián)起來的地方。
我們進入源碼中:
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); } } } }
代碼一行一行看是不是覺得很累,我給你總結(jié)好了。
上面的方法分為以下七個步驟:
首先取得 @EnableFeignClients
註解的所有屬性,主要為了拿到掃描套件路徑(basePackages
);#因為一般不會在 @EnableFeignClients
註解中配置clients屬性,所以會進入到clients屬性為空時的邏輯;- ##然後透過
getScanner() 方法獲取掃描器:
ClassPathScanningCandidateComponentProvider,並將上下文
AnnotationConfigServletWebServerApplicationContext作為掃描器的
ResourceLoader;
- 接著給掃描器
ClassPathScanningCandidateComponentProvider ;新增一個註解過濾器(AnnotationTypeFilter),只過濾出包含
@FeignClient註解的
BeanDefinition;
- #;
方法取得##再透過
getBasePackages(metadata) @EnableFeingClients###註解中的指定的套件掃描路徑或掃描類別;如果沒有取得到,則預設(shè)掃描啟動類別所在的套件路徑;###### 然後進入到核心邏輯:透過 scanner.findCandidateComponents(basePackage)
方法從套件路徑下掃描出所有標註了@FeignClient
註解並符合條件組裝的介面;最後將 FeignClientConfiguration
在BeanDefinitionRegistry
中註冊一下,再對FeignClient
做真正的註冊操作。
總結(jié)
在openfeign
原始碼中的@Import註解在這裡的作用就是將掃描到帶有FeignClient
註解的全部介面類別以bean的形式註冊到spring IOC容器中。
再來強調(diào)@Import註解使用方式:
@Import
一個普通類別spring會將該類別載入到spring容器中@Import
一個類,該類別實作了ImportBeanDefinitionRegistrar
接口,在重寫的registerBeanDefinitions
方法裡面,能拿到BeanDefinitionRegistry
的註冊器,可以手工往beanDefinitionMap
中註冊beanDefinition
#@Import
一個類別該類別實作了ImportSelector
重寫selectImports
方法此方法傳回了String[]陣列的對象,陣列裡面的類別都會注入到spring容器當中。
好了,今天就分享這麼多。這個openfeign裡面還有很多很有意思的地方,我們下次再分享吧!
以上是Spring Cloud原始碼分析:第一篇的詳細內(nèi)容。更多資訊請關(guān)注PHP中文網(wǎng)其他相關(guān)文章!

熱AI工具

Undress AI Tool
免費脫衣圖片

Undresser.AI Undress
人工智慧驅(qū)動的應用程序,用於創(chuàng)建逼真的裸體照片

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

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發(fā)環(huán)境

Dreamweaver CS6
視覺化網(wǎng)頁開發(fā)工具

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

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

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

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

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

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

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

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

隨著微服務架構(gòu)的廣泛應用,如何有效地監(jiān)控和警告成為了開發(fā)人員和維運人員面臨的問題之一。本文將重點放在SpringCloud微服務架構(gòu)下實踐監(jiān)控和警告的具體方法。一、監(jiān)控指標的選擇在進行監(jiān)控前,首先需要確定需要監(jiān)控的指標。常見的指標包括:CPU使用率、記憶體使用率、網(wǎng)路頻寬、磁碟空間、HTTP請求的回應時間、服務呼叫的次數(shù)和延遲等。這些指標可透過各種監(jiān)控工
