<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          掀起Spring擴(kuò)展點(diǎn)的蓋頭來ImportBeanDefinitionRegistrar分析與實(shí)戰(zhàn)

          共 8168字,需瀏覽 17分鐘

           ·

          2022-02-20 09:42

          本文開始,我們將系統(tǒng)地對(duì)Spring框架的擴(kuò)展點(diǎn)進(jìn)行學(xué)習(xí),通過案例分析與圖例結(jié)合,step by step地對(duì)Spring看似神秘的擴(kuò)展點(diǎn)的機(jī)理與應(yīng)用進(jìn)行研究。

          首先通過一張圖對(duì)Spring框架各種擴(kuò)展點(diǎn)的調(diào)用順序(Bean生命周期)進(jìn)行先入為主的概覽。

          e13e4845e38f83ce6cb6f4aad94e79f7.webp

          可以看到圖片的一開始便是Spring對(duì)Bean定義(BeanDefinition)進(jìn)行解析和注冊(cè),Bean的注冊(cè)主要就是通過ImportBeanDefinitionRegistrar實(shí)現(xiàn)的。

          Spring框架主要就是通過ImportBeanDefinitionRegistrar實(shí)現(xiàn)對(duì)bean的動(dòng)態(tài)注冊(cè)。源碼如下:

          ????public?interface?ImportBeanDefinitionRegistrar?{
          ????????//?通過解析給定的注解元信息,向Spring容器中注冊(cè)Bean定義
          ????????default?void?registerBeanDefinitions(AnnotationMetadata?importingClassMetadata,?BeanDefinitionRegistry?registry,
          ????????????????BeanNameGenerator?importBeanNameGenerator)?{

          ????????????registerBeanDefinitions(importingClassMetadata,?registry);
          ????????}
          ????????default?void?registerBeanDefinitions(AnnotationMetadata?importingClassMetadata,?BeanDefinitionRegistry?registry)?{
          ????????}
          ????}

          實(shí)現(xiàn)ImportBeanDefinitionRegistrar接口的類的都會(huì)被ConfigurationClassPostProcessor處理,ConfigurationClassPostProcessor實(shí)現(xiàn)了BeanFactoryPostProcessor接口,所以ImportBeanDefinitionRegistrar中動(dòng)態(tài)注冊(cè)的bean是優(yōu)先于依賴其的bean初始化的,同時(shí)它也可以被aop、validator等機(jī)制處理。

          編碼實(shí)現(xiàn)手動(dòng)Bean注入

          ?

          日常的業(yè)務(wù)開發(fā)中,我們很少會(huì)通過ImportBeanDefinitionRegistrar來對(duì)bean進(jìn)行注入。

          ?
          ?

          而是通過xml文件聲明或者注解如:@Component、@Service、@Bean等方式對(duì)bean進(jìn)行注入和聲明。

          ?
          ?

          那么什么場(chǎng)景下才需要通過ImportBeanDefinitionRegistrar注冊(cè)并注入bean呢?

          ?

          在中間件開發(fā)場(chǎng)景下,就會(huì)用到手動(dòng)bean注入。原因在于中間件/框架的開發(fā)者并不知道調(diào)用方/框架使用者是通過什么方式對(duì)bean進(jìn)行注入的。

          當(dāng)然我們也可以讓使用者們顯式的對(duì)框架中的bean進(jìn)行定義,但是這樣就顯著的增加了工作量和出錯(cuò)率,因此對(duì)于框架開發(fā)而言,常常通過ImportBeanDefinitionRegistrar實(shí)現(xiàn)bean的隱式注入和聲明,減少調(diào)用方整合框架的復(fù)雜度。

          ?

          我們通過一個(gè)模擬場(chǎng)景來介紹一下如何通過編碼實(shí)現(xiàn)bean的手動(dòng)隱式注入。、

          ?

          1、定義ImportBeanDefinitionRegistrar實(shí)現(xiàn)類

          首先定義一個(gè)實(shí)現(xiàn)ImportBeanDefinitionRegistrar接口的類,并編寫bean注冊(cè)邏輯。

          ????public?class?MyBeanDefinationRegistry?implements?ImportBeanDefinitionRegistrar,?ResourceLoaderAware,?BeanFactoryAware?{

          ????????private?BeanFactory?beanFactory;
          ????????private?ResourceLoader?resourceLoader;

          ????????@Override
          ????????public?void?setBeanFactory(BeanFactory?beanFactory)?throws?BeansException?{
          ????????????this.beanFactory?=?beanFactory;
          ????????}

          ????????@Override
          ????????public?void?setResourceLoader(ResourceLoader?resourceLoader)?{
          ????????????this.resourceLoader?=?resourceLoader;
          ????????}

          ????????@Override
          ????????public?void?registerBeanDefinitions(AnnotationMetadata?importingClassMetadata,?BeanDefinitionRegistry?registry)?{

          ????????????MyClassPathBeanDefinitionScanner?scanner?=?new?MyClassPathBeanDefinitionScanner(registry,?false);
          ????????????scanner.setResourceLoader(resourceLoader);
          ????????????scanner.registerFilters();
          ????????????scanner.doScan("com.spring.framework");

          ????????????GenericBeanDefinition?genericBeanDefinition?=?new?GenericBeanDefinition();
          ????????????genericBeanDefinition.setBeanClass(TestBean.class);
          ????????????genericBeanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
          ????????????registry.registerBeanDefinition("testBean",?genericBeanDefinition);
          ????????}
          ????}

          重點(diǎn)關(guān)注 「BeanDefinitionRegistry」 方法,這里提供了兩種bean掃描方式。

          方式1:基于包路徑的掃描

          ????????MyClassPathBeanDefinitionScanner?scanner?=?
          ????????????new?MyClassPathBeanDefinitionScanner(registry,?false);
          ????????scanner.setResourceLoader(resourceLoader);
          ????????scanner.registerFilters();
          ????????scanner.doScan("com.spring.framework");

          1. 自定義一個(gè)ClassPathBeanDefinitionScanner實(shí)例,并將bean定義注冊(cè)器BeanDefinitionRegistry引用傳遞進(jìn)去,這是一種委托機(jī)制;
          2. 設(shè)置ResourceLoader,ResourceLoader的引用通過ResourceLoaderAware獲得,并指向當(dāng)前類的成員變量;
          3. 調(diào)用registerFilters方法(該方法為自定義方法,本質(zhì)是調(diào)用了addIncludeFilter),讓Spring去掃描帶有特定標(biāo)志的類進(jìn)行管理與加載;(具體的代碼稍后進(jìn)行分析);
          4. 調(diào)用doScan,傳遞需要掃描的包路徑,這個(gè)路徑就是框架開發(fā)者自定義的包路徑,該路徑下存放的就是框架本身的bean,「這個(gè)路徑是完全由框架的開發(fā)者決定的,而且我們一般可以認(rèn)為,該路徑一旦定義就不會(huì)更改。并且該路徑也不適合暴露給框架的調(diào)用者」

          方式2:直接注冊(cè)BeanDefinition

          ????????GenericBeanDefinition?genericBeanDefinition?=?new?GenericBeanDefinition();
          ????????genericBeanDefinition.setBeanClass(TestBean.class);
          ????????genericBeanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
          ????????registry.registerBeanDefinition("testBean",?genericBeanDefinition);

          方式2比較簡(jiǎn)單,但是相對(duì)的也比方式1繁瑣。

          ?

          TestBean 是模擬的一個(gè)框架的內(nèi)部bean組件,實(shí)際開發(fā)中可以根據(jù)需要填充必要的屬性和方法,這里只是作為演示。

          ?
          ????public?class?TestBean?{
          ????}

          通過聲明GenericBeanDefinition,并為其添加需要注冊(cè)的Bean的class,scope(單例or多例),beanName等屬性,具體的屬性可以看下圖:

          7be51fb94995077ed5470c240adec9a3.webp

          最后通過 「registry.registerBeanDefinition」 將設(shè)置好屬性的GenericBeanDefinition注冊(cè),并設(shè)置beanName;

          對(duì)比方式1方式2

          通過代碼我們可以直觀的看到,方式1比方式2更加方便,可以實(shí)現(xiàn)批量bean的掃描與注入;

          而方式2則需要逐個(gè)bean進(jìn)行注入,但是相對(duì)的,方式2也更加靈活,能夠?qū)崿F(xiàn) 「細(xì)粒度」 的beanDefinition聲明和定義。

          2、定義ClassPathBeanDefinitionScanner實(shí)現(xiàn)類

          通過定義ClassPathBeanDefinitionScanner的實(shí)現(xiàn)類,告訴Spring需要對(duì)哪些類進(jìn)行管理(addIncludeFilter)以及不需要關(guān)注哪些類(addExcludeFilter)。

          ????public?class?MyClassPathBeanDefinitionScanner?extends?ClassPathBeanDefinitionScanner?{

          ????????public?MyClassPathBeanDefinitionScanner(BeanDefinitionRegistry?registry,?boolean?useDefaultFilters)?{
          ????????????super(registry,?useDefaultFilters);
          ????????}

          ????????/**
          ????????*?比較重要的一個(gè)點(diǎn)就是registerFilters()這個(gè)方法,
          ????????*?在里面我們可以定義讓Spring去掃描帶有特定標(biāo)志的類選擇進(jìn)行管理或者是選擇不管理;
          ????????*?通過addIncludeFilter()方法和通過addExcludeFilter()方法;
          ????????*/
          ????????protected?void?registerFilters()?{
          ????????????/**
          ????????????*??TODO?addIncludeFilter??滿足任意includeFilters會(huì)被加載
          ????????????*/
          ????????????addIncludeFilter(new?AnnotationTypeFilter(SnoWalkerAutoInject.class));
          ????????}

          ????????@Override
          ????????protected?Set<BeanDefinitionHolder>?doScan(String...?basePackages)?{
          ????????????return?super.doScan(basePackages);
          ????????}
          ????}

          可以看到,掃描器ClassPathBeanDefinitionScanner掃描類路徑上的需要被管理的類,通過BeanFactory創(chuàng)建Bean給ApplicationComtext(Spring容器)管理;

          registerFilters分析

          registerFilters是自定義的方法,核心的邏輯就是通過addIncludeFilter添加了一個(gè)包掃描的規(guī)則:

          這里是通過注解類型的Filter通知Spring容器對(duì)添加了SnoWalkerAutoInject自定義注解的bean進(jìn)行管理。

          我們可以看到,自定義的MyClassPathBeanDefinitionScanner重寫了父類的doScan方法,本質(zhì)上調(diào)用了父類的doScan,以實(shí)現(xiàn)對(duì)指定路徑下的bean進(jìn)行掃描。

          最終實(shí)際上是在ApplicationContext中調(diào)用了doScan,實(shí)現(xiàn)了對(duì)bean定義的掃描及實(shí)例化,我們可以看一下源碼實(shí)現(xiàn):

          ?/**
          ??*?Create?a?new?AnnotationConfigApplicationContext,?scanning?for?components
          ??*?in?the?given?packages,?registering?bean?definitions?for?those?components,
          ??*?and?automatically?refreshing?the?context.
          ??*?@param?basePackages?the?packages?to?scan?for?component?classes
          ??*/
          ?public?AnnotationConfigApplicationContext(String...?basePackages)?{
          ??this();
          ??scan(basePackages);
          ??refresh();
          ?}

          AnnotationConfigApplicationContext構(gòu)造方法中,對(duì)package進(jìn)行了掃描,并調(diào)用refresh方法對(duì)bean進(jìn)行初始化和實(shí)例化。

          3、自定義注解

          自定義注解,并添加到需要裝載到Spring容器中的框架類上:

          ????@Documented
          ????@Inherited
          ????@Retention(RetentionPolicy.RUNTIME)
          ????@Target({ElementType.TYPE,?ElementType.FIELD,?ElementType.METHOD,?ElementType.PARAMETER})
          ????public?@interface?SnoWalkerAutoInject?{
          ????}

          定義幾個(gè)模擬的框架類,用以模擬框架的邏輯。實(shí)際的開發(fā)中,我們可以按照需求的實(shí)際需要,開發(fā)框架代碼,并標(biāo)記自定義的注解。

          ????@SnoWalkerAutoInject
          ????public?class?FrameWorkConfigA?{

          ????????public?FrameWorkConfigA()?{
          ????????????System.out.println("自定義框架組件A-初始化邏輯");
          ????????}
          ????}

          ????@SnoWalkerAutoInject
          ????public?class?FrameWorkConfigB?{

          ????????public?FrameWorkConfigB()?{
          ????????????System.out.println("自定義框架組件B-初始化邏輯");
          ????????}
          ????}

          ????@SnoWalkerAutoInject
          ????public?class?FrameWorkConfigC?{

          ????????public?FrameWorkConfigC()?{
          ????????????System.out.println("自定義框架組件C-初始化邏輯");
          ????????}
          ????}

          4、配置ImportBeanDefinitionRegistrar實(shí)現(xiàn)類

          如何使用自定義的ImportBeanDefinitionRegistrar實(shí)現(xiàn)類對(duì)bean進(jìn)行裝載呢?

          最終我們還是需要定義一個(gè)配置類,通過@Import注解配置ImportBeanDefinitionRegistrar實(shí)現(xiàn)。

          ????@Configuration
          ????@Import(MyBeanDefinationRegistry.class)
          ????@ComponentScan("com.spring.framework")
          ????public?class?MyConf?{
          ????}
          1. MyConf是自定義的配置類,標(biāo)注了 @Configuration 注解。
          2. 通過@Import將實(shí)現(xiàn)了ImportBeanDefinitionRegistrar接口的MyBeanDefinationRegistry包含進(jìn)來;
          3. 添加掃描包,以方便spring對(duì)該包下的類進(jìn)行掃描并進(jìn)行選擇性的裝載;

          4、測(cè)試

          編寫測(cè)試類:

          ????public?class?App?{

          ????????public?static?void?main(String[]?args)?{
          ????????????ApplicationContext?applicationContext?=?new?AnnotationConfigApplicationContext("com.spring");

          ????????????final?TestBean?testBean?=?(TestBean)?applicationContext.getBean("testBean");
          ????????????System.out.println(testBean.getClass());
          ????????}
          ????}

          1. 首先我們聲明并初始化一個(gè)AnnotationConfigApplicationContext容器;
          2. 接著從容器中通過BeanName獲取通過GenericBeanDefinition定義的TestBean實(shí)例,打印其Class類型;
          3. 觀察日志輸出,期望能夠看到框架代碼FrameWorkConfigA、FrameWorkConfigB、FrameWorkConfigC的構(gòu)造方法日志打印,并看到TestgBean的Class類型打印。

          運(yùn)行測(cè)試類,觀察控制臺(tái)日志輸出:

          ????自定義框架組件A-初始化邏輯
          ????自定義框架組件B-初始化邏輯
          ????自定義框架組件C-初始化邏輯

          ????class?com.spring.TestBean

          可以看到符合預(yù)期,這表明,通過ImportBeanDefinitionRegistrar自定義手動(dòng)bean注入符合預(yù)期。

          總結(jié)

          本文我們?nèi)獙?duì)ImportBeanDefinitionRegistrar在Spring容器裝載bean的過程進(jìn)行了綜述,并通過一個(gè)模擬框架開發(fā)的案例,對(duì)如何通過ImportBeanDefinitionRegistrar實(shí)現(xiàn)bean的自定義注入進(jìn)行了代碼級(jí)別的講解和分析。

          如果在實(shí)際的開發(fā)案例中需要實(shí)現(xiàn)自定義的bean注入,減少調(diào)用方整合的復(fù)雜度,那么我們完全可以通過本文講解的方式,利用ImportBeanDefinitionRegistrar擴(kuò)展點(diǎn)實(shí)現(xiàn)。

          下期預(yù)告:

          下期我們將分析講解BeanPostProcessor擴(kuò)展點(diǎn)在Spring框架中的作用,并講解BeanPostProcessor在實(shí)戰(zhàn)開發(fā)中的使用。


          瀏覽 119
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  秘 看片黄全部免费 | 青春草在线视频观看 | 色婷婷视频一区二区 | 无码肉厚美臂小早川怜子 | 最新在线成人网站 |