spring容器的入口,spring容器_Spring 容器的啟動過程探秘

 2023-10-21 阅读 31 评论 0

摘要:一. 前言Spring家族特別龐大,對于開發人員而言,要想全面征服Spring家族,得花費不少的力氣。俗話說,打蛇打七寸,那么Spring家族的“七寸”是什么呢?我心目中的答案一直都是 Spring Framework!本篇文章記錄我自己在學習Spring

一. 前言

Spring家族特別龐大,對于開發人員而言,要想全面征服Spring家族,得花費不少的力氣。俗話說,打蛇打七寸,那么Spring家族的“七寸”是什么呢?我心目中的答案一直都是 Spring Framework!

本篇文章記錄我自己在學習Spring Framework的過程中的一小部分源碼解讀和梳理,來談一談Spring 容器在啟動過程中是如何掃描Bean的。

二. 學習方法論

我相信每個想變成優秀的開發人員都想弄懂Spring源碼,我亦如此。于是通過很多途徑來找Spring源碼的學習資料、買書、看視頻等等。到頭來發現只有自己靜下心來一步一步跟著源碼調試,一行一行的深入理解,才能深入理解Spring的奧妙!這個過程很枯燥,但優秀的獵手最能耐得住寂寞和浮躁!

我們知道,Spring容器的啟動方式有多種:XML文件、注解、Java Config。在實際的使用中并不是選擇其中某一種,而是相互搭配。其底層的容器啟動過程是一樣的,只是入口變了而已。另外,學習Spring的最佳方式就是自己將源碼工程構建出來,這樣便于源碼閱讀、備注、修改。構建出來的工程長這樣:

66d32283bcbc401bdb7a1c0291ca2ca7.png

三. 代碼入口

話不多說直接開干!代碼入口如下:

@Configuration@ComponentScan("com.leon.funddatahouse")public?class?Config?{}public?class?MyApplication?{????public?static?void?main(String[]?args)?{????????//?我們基于注解的方式????????AnnotationConfigApplicationContext?annotationConfigApplicationContext?=?new?AnnotationConfigApplicationContext(Config.class);????????//?如果基于XML文件配置,則也可以如下:????????//?ClassPathXmlApplicationContext?classPathXmlApplicationContext?=?new?ClassPathXmlApplicationContext("spring-context.xml");????}}

spring容器的入口?在構造方法中,總共做了3件事情。這三件事情包括了整個Spring容器啟動的所有過程!啃碎他們,便成功了一半!

public?AnnotationConfigApplicationContext(Class>...?componentClasses)?{????????//?1.調用默認構造方法,進行容器環境的準備????????this();????????//?2.基于配置類注冊相關信息????????register(componentClasses);????????//?3.刷新整個容器????????refresh();????}

四. 解析之前

在解析之前,先將容器和BeanFactory的UML類圖放出。原因在于它們擔任的角色、具備的功能太多太強大了,同時這也增加了源碼理解的難度。因此這里先放出UML類圖作為手冊查看,便于理解源碼。

4.1 容器UML類圖

6b36f0ec5120ab389b2772da92af3948.png

4.2 BeanFactoryUML類圖

62aaa9e70f2a892f5ab6381a47f4d1bd.png

五. 源碼解析

5.1 構造方法解析

5.1.1 初始化容器中的BeanFactory

在構造方法中,顯式的調用了this(),既無參構造方法:

public?AnnotationConfigApplicationContext()?{????//?1.實例化容器中的reader.?后面會詳細解析????this.reader?=?new?AnnotatedBeanDefinitionReader(this);????//?2.實例化容器中的scanner.后面會詳細解析????this.scanner?=?new?ClassPathBeanDefinitionScanner(this);}

乍看一眼,這個無參構造方法做了兩件事情,其實不然。它實際上等同于:

public?AnnotationConfigApplicationContext()?{????//?1.調用父類構造方法????super();????//?2.實例化容器中的reader.?后面會詳細解析????this.reader?=?new?AnnotatedBeanDefinitionReader(this);????//?3.實例化容器中的scanner.后面會詳細解析????this.scanner?=?new?ClassPathBeanDefinitionScanner(this);}

這一點很關鍵, 如果沒有意識到這里隱形調用了父類構造方法的話, 那么接下來的路沒法走, 因為在父類構造器中做了一件大事情:

//?在父類的構造方法中,?創建了容器中的BeanFactory.至此,容器中有了第一個程序創建的屬性:beanFactorypublic?GenericApplicationContext()?{????//?初始化容器的beanFactory,類型為DefaultListableBeanFactory????this.beanFactory?=?new?DefaultListableBeanFactory();}

BeanFactory 和 FacotryBean的區別, 請點擊這里

5.1.2 實例化容器中的Reader

spring容器什么時候初始化、reader最主要的目的是用于輔助注冊BeanDefinition,其具體的使用后文在介紹,這里我們只需知道它包含了哪些東西。

//?入參registry就是容器本身。因為通過上面的UML類圖可以發現,容器間接繼承了BeanDefinitionRegistrypublic?AnnotatedBeanDefinitionReader(BeanDefinitionRegistry?registry)?{????// getOrCreateEnvironment()?方法最主要是獲取環境。實際類型其實默認的就是StandardEnvironment類。這里的環境包括兩方面:????// 1.systemEnvironment:操作系統環境。這樣,Spring就可以獲取到操作系統、CPU核心數等操作系統本身的數據。????// 2.systemProperties:JVM的環境變量。這樣,Spring就可以獲取到JVM的基礎數據,比如我們在啟動參數中手動設置的環境變量等。????this(registry,?getOrCreateEnvironment(registry));}

這里通過this() 調用了reader內部另一個構造方法:

public?AnnotatedBeanDefinitionReader(BeanDefinitionRegistry?registry,?Environment?environment)?{????Assert.notNull(registry,?"BeanDefinitionRegistry?must?not?be?null");????Assert.notNull(environment,?"Environment?must?not?be?null");????//?設置registry,已經知道它的就是容器本身:AnnotationConfigApplicationContext????this.registry?=?registry;????//?創建條件處理器????this.conditionEvaluator?=?new?ConditionEvaluator(registry,?environment,?null);????//?非常關鍵!提前往容器中注冊一些必要的后置處理器????AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);}

這個構造方法很重要, 因為它涉及到spring容器當中的兩個重要成員:條件解析器和后置處理器!

5.1.2.1 實例化條件處理器

相信熟悉Spring的人一定都知道或用過@ConditionalOnBean / @ConditionalOnClass 等條件注解.而這些條件注解的解析就是ConditionEvaluator.

public?ConditionEvaluator(@Nullable?BeanDefinitionRegistry?registry,????????@Nullable?Environment?environment,?@Nullable?ResourceLoader?resourceLoader)?{????//?實際上是委托給內部類ConditionContextImpl????this.context?=?new?ConditionContextImpl(registry,?environment,?resourceLoader);}//?------------分割線------------------//?內部的ConditionContextImpl構造器public?ConditionContextImpl(@Nullable?BeanDefinitionRegistry?registry,????????@Nullable?Environment?environment,?@Nullable?ResourceLoader?resourceLoader)?{????//?再說一遍,registry的實際類型就是?AnnotationConfigApplicationCont????this.registry?=?registry;????//?獲取beanFactory,我們也知道了beanFactory其實就是?ConfigurableListableBeanFactory????this.beanFactory?=?deduceBeanFactory(registry);????//?從容器中獲取environment,前面介紹過,容器中的environment的封裝類是?StandardEnvironment????this.environment?=?(environment?!=?null???environment?:?deduceEnvironment(registry));????//?資源加載器.?通過UML類圖可以發現,resourceLoader就是容器,?因為容器間接繼承了ResourceLoader????this.resourceLoader?=?(resourceLoader?!=?null???resourceLoader?:?deduceResourceLoader(registry));????//?類加載器. 實際上就是獲取beanFactory的類加載器。理應如此,容器當中的類加載器肯定要一致才行????this.classLoader?=?deduceClassLoader(resourceLoader,?this.beanFactory);}

后面在解析BeanDefinition時我們還會遇到ConditionEvaluator, 其具體源碼解析會用專門的文章來解析,本篇文章我們只需要知道它的作用即可.

5.1.2.2 注冊一部分后置處理器

ConditionEvaluator初始化完成之后,接下來就特別重要了,因為在這里將提前注入一些后置處理器:

public?static?void?registerAnnotationConfigProcessors(BeanDefinitionRegistry?registry)?{????//?空殼方法,實際委托給重載的方法????registerAnnotationConfigProcessors(registry,?null);}

docker安裝java環境?重載的方法如下(高能預警):

public?static?Set?registerAnnotationConfigProcessors(????????BeanDefinitionRegistry?registry,?@Nullable?Object?source)?{????//?獲取容器中的beanFactory,通過前面的解析,我們知道,這里一定會獲取到。因此將進入if分支????DefaultListableBeanFactory?beanFactory?=?unwrapDefaultListableBeanFactory(registry);????if?(beanFactory?!=?null)?{????????//?此時beanFactory的屬性dependencyComparator為null,因為初始化過程中,內部成員變量如果沒有默認值,則默認為null,????????//?所以如果第一次進來, 這里的判斷一定成立,對dependencyComparator進行設置。????????//?AnnotationAwareOrderComparator繼承了OrderComparator,????????//?因此可以對實現了Ordered接口、打上@Order或者@Priority注解的類進行排序。????????//?也就是說,在這里設置beanFactory中的orderComparator,以支持解析bean的排序功能。????????if?(!(beanFactory.getDependencyComparator()?instanceof?AnnotationAwareOrderComparator))?{????????????beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);????????}????????// beanFactory初始化時,默認為SimpleAutowireCandidateResolver,因此第一次進來時這里的判斷也一定成立。????????// ContextAnnotationAutowireCandidateResolver最主要的作用就是支持@Lazy注解的類的處理。????????if?(!(beanFactory.getAutowireCandidateResolver()?instanceof?ContextAnnotationAutowireCandidateResolver))?{????????????beanFactory.setAutowireCandidateResolver(new?ContextAnnotationAutowireCandidateResolver());????????}????}????//?初始化一個bdh容器,用于盛放接下來將解析出來的后置處理器的bd。????Set?beanDefs?=?new?LinkedHashSet<>(8);????//?容器在第一次初始化時,內部一個bd都沒有的。????//?也就是說,從這里開始,容器將第一次裝載bd,而這里的這些bd都是spring自帶的后置處理器。????//?獲取并注冊ConfigurationClassPostProcessor后置處理器?的bd????if?(!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME))?{????????RootBeanDefinition?def?=?new?RootBeanDefinition(ConfigurationClassPostProcessor.class);????????def.setSource(source);????????beanDefs.add(registerPostProcessor(registry,?def,?CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));????}????//?獲取并注冊AutowiredAnnotationBeanPostProcessor后置處理器?的bd????if?(!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME))?{????????RootBeanDefinition?def?=?new?RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);????????def.setSource(source);????????beanDefs.add(registerPostProcessor(registry,?def,?AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));????}????//?獲取并注冊CommonAnnotationBeanPostProcessor后置處理器?的bd????if?(jsr250Present?&&?!registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME))?{????????RootBeanDefinition?def?=?new?RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);????????def.setSource(source);????????beanDefs.add(registerPostProcessor(registry,?def,?COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));????}????//?獲取并注冊PersistenceAnnotationBeanPostProcessor后置處理器?的bd????if?(jpaPresent?&&?!registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME))?{????????RootBeanDefinition?def?=?new?RootBeanDefinition();????????try?{????????????def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,????????????????????AnnotationConfigUtils.class.getClassLoader()));????????}????????catch?(ClassNotFoundException?ex)?{????????????throw?new?IllegalStateException(????????????????????"Cannot?load?optional?framework?class:?"?+?PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,?ex);????????}????????def.setSource(source);????????beanDefs.add(registerPostProcessor(registry,?def,?PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));????}????//?獲取并注冊EventListenerMethodProcessor后置處理器?的bd????if?(!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME))?{????????RootBeanDefinition?def?=?new?RootBeanDefinition(EventListenerMethodProcessor.class);????????def.setSource(source);????????beanDefs.add(registerPostProcessor(registry,?def,?EVENT_LISTENER_PROCESSOR_BEAN_NAME));????}????//?獲取并注冊DefaultEventListenerFactory后置處理器?的bd????if?(!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME))?{????????RootBeanDefinition?def?=?new?RootBeanDefinition(DefaultEventListenerFactory.class);????????def.setSource(source);????????beanDefs.add(registerPostProcessor(registry,?def,?EVENT_LISTENER_FACTORY_BEAN_NAME));????}????return?beanDefs;}

這個方法首次出現了BeanDefinition這個類. Spring的BeanDefinition相當于Java的Class

通過該方法之后, beanFactory中就存在了以上6個bd:

4a332c02e0aac47a004095be21fbe523.png

曾經有人跟我說, 掌握了Spring的后置處理器, 那么整個Spring就掌握了10%! 可見其重要性. 但是在這里先不展開后置處理器(太多了),本篇文章的主線是容器啟動過程。

5.1.2.3 reader初始化過程小結

到這里reader部分的初始化終于完成了。總結一下,reader的初始化主要干了這些事情:1.創建并設置容器當中的Environment屬性。即默認為StandardEnvironment類。2.創建并設置容器當中的條件解析器,即ConditionEvaluator,其內部實際委托給內部類ConditionContextImpl。3.注冊6個后置處理器到容器當中。注意這里僅是生成了后置處理器的BeanDefinition。還并沒有進行bean解析和后置處理的執行。

5.1.3 實例化容器中的Scanner

解析完reader之后,繼續解析scanner。這里的scanner的實際類型是ClassPathBeanDefinitionScanner。它最主要的目的就是掃描類路徑下所有的class文件能否解析為bd。其最終調用的構造方法如下:

public?ClassPathBeanDefinitionScanner(BeanDefinitionRegistry?registry)?{????//?1.委托給內部的另一個構造方法????this(registry,?true);}//?------------------------分割線-------------------------public?ClassPathBeanDefinitionScanner(BeanDefinitionRegistry?registry,?boolean?useDefaultFilters)?{????//?2.又委托給內部的另一個構造方法?>_

5.1.3.1 registerDefaultFilters()方法

Spring boot?從最終的構造方法我們知道, Scanner在掃描的過程中,會使用過濾策略,并且使用了默認的過濾策略.默認策略就是以下這個方法解析.

protected?void?registerDefaultFilters()?{????//?掃描@Component注解的類????this.includeFilters.add(new?AnnotationTypeFilter(Component.class));????ClassLoader?cl?=?ClassPathScanningCandidateComponentProvider.class.getClassLoader();????try?{????????//?掃描所有@ManageBean的類????????this.includeFilters.add(new?AnnotationTypeFilter(????????????????((Class?extends?Annotation>)?ClassUtils.forName("javax.annotation.ManagedBean",?cl)),?false));????????logger.trace("JSR-250?'javax.annotation.ManagedBean'?found?and?supported?for?component?scanning");????}????catch?(ClassNotFoundException?ex)?{????}????try?{????????//?掃描所有@Named的類????????this.includeFilters.add(new?AnnotationTypeFilter(????????????????((Class?extends?Annotation>)?ClassUtils.forName("javax.inject.Named",?cl)),?false));????????logger.trace("JSR-330?'javax.inject.Named'?annotation?found?and?supported?for?component?scanning");????}????catch?(ClassNotFoundException?ex)?{????}}

這里的一個知識點:@ManageBean和@Named的作用和@Component是一樣的。只是我們通常習慣使用@Component。

為什么這里沒有添加默認掃描@Service、@Repository、@Controller呢?原因很簡單,這些注解都間接繼承了@Component了。到這里,scanner解析完畢,它做的最主要的事情就是添加默認的過濾器策略以便在后續中可以掃描出@Component注解的類。

六 默認構造方法小結

現在我們再來看一下構造方法:

public?AnnotationConfigApplicationContext(Class>...?componentClasses)?{????//?1.調用默認構造方法,進行容器環境的準備????this();????register(componentClasses);????refresh();}

從入口看, 就只有這三行代碼, 但其中的第一行,調用默認構造方法就做了這么多準備工作,其中也牽扯出了一些Spring整個體系中最重要的幾個組件,比如BeanFactory / BeanDefinition / BeanDefinitionReader / BeanDefinitionScanner / Environment / ConditionEveluator / PostProcessor等等.隨便拿一個出來都夠喝一壺! 這些點會各個擊破, 但不是本篇文章的重點,本篇文章的重點是先梳理整個啟動過程的第一步: 構造方法的執行過程.


本文來源:http://r6f.cn/b47K

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/4/156547.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息