<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>

          @Configuration 類型的 class 需要知道的細節(jié)

          共 5361字,需瀏覽 11分鐘

           ·

          2021-12-31 15:41

          點擊下方“IT牧場”,選擇“設(shè)為星標”

          知識點收獲

          通過本文,你能收獲什么?

          • @Configuration.class文件如何被spring加載的?

          • @Configuration.class文件如何被轉(zhuǎn)化為BeanDefinition放入spring容器的?

          • @Configuration的BeanDefinition如何轉(zhuǎn)化為ConfigurationClass對象的

          • ConfigurationClass對象屬性在哪里開始被解析的?

          • @Configuration的BeanDefinition的beanClass值何時變成proxy代理類的,為什么要變

          • @Configuration類最終會生成cglib proxy代理類,這個由@Configuration類生成的cglib proxy代理類如何實例化的

          • 我們能從中得到的擴展點有哪些

          @Configuration注解作用

          @Configuration標識的類有這些特性:可以聲明多個@Bean方法,且在運行時被spring容器處理來生成BeanDefinition。@Configuration類是被AnnotationConfigWebApplicationContext啟動(bootstrap)處理流程的。聲明方式如下代碼

          @Configuration
          ?public?class?AppConfig?{
          ????@Bean
          ????public?MyBean?myBean()?{
          ????????//?instantiate,?configure?and?return?bean?...
          ????}
          }

          @Configuration class存在如下約束,如下圖

          @Configuration class源碼加載分析

          @Configuration相關(guān)的類

          如果IDE你使用的Idea,double Shift鍵,輸入ConfigurationClass,會出現(xiàn)一長列的類,

          這些類分工協(xié)作,從加載標識@Configuration的.class文件,到生成BeanDefinition,再到生成ConfigurationClass對象,再到解析ConfigurationClass對象,即解析@ConfigurationClass class類中@Bean、@Import、@ImportResource等注解

          ConfigurationClass:?專門表示@Configuration class。存儲解析@Configuration class的信息

          ConfigurationClassPostProcessor:?專門用于處理@Configuration class的BeanDefinitoinRegistryPostProcessor(BeanFactoryPostProcessor子類)

          AnnotationConfigUtils?:專門注冊BeanPostProcessor和BeanFactoryPostProcessor到spring容器

          @Configuration解析過程

          ConfigurationClassPostProcessor實例創(chuàng)建過程

          我們剛說過,ConfigurationClassPostProcessor是處理@Configuration class的核心組件,它是BeanFactoryPostProcessor類型子類且是BeanDefinitoinRegistryPostProcessor類型子類。BeanFactoryPostProcessor是AbstractApplicationContext's post-processor handling技術(shù)的規(guī)范接口,在項目啟動較早時段,它便開始工作?;蛘哒f任何spring boot項目啟動時都是走post-processor handling處理邏輯,這個邏輯的入口在PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()方法,代碼如下

          PostProcessorRegistrationDelegate?class
          public?static?void?invokeBeanFactoryPostProcessors(
          ????????ConfigurableListableBeanFactory?beanFactory,?List<BeanFactoryPostProcessor>?beanFactoryPostProcessors)?
          {
          ????···
          ????//?Invoke?BeanDefinitionRegistryPostProcessors?first,?if?any.
          ????if?(beanFactory?instanceof?BeanDefinitionRegistry)?{
          ????????BeanDefinitionRegistry?registry?=?(BeanDefinitionRegistry)?beanFactory;
          ????????List?currentRegistryProcessors?=?new?ArrayList<>();

          ????????//?First,?invoke?the?BeanDefinitionRegistryPostProcessors?that?implement?PriorityOrdered.
          ????????String[]?postProcessorNames?=
          ????????????????beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class,?true,?false);?//?(1)
          ????????for?(String?ppName?:?postProcessorNames)?{
          ????????????if?(beanFactory.isTypeMatch(ppName,?PriorityOrdered.class))?{
          ????????????????currentRegistryProcessors.add(beanFactory.getBean(ppName,?BeanDefinitionRegistryPostProcessor.class));?//?(2)
          ????????????}
          ????????}
          ????????sortPostProcessors(currentRegistryProcessors,?beanFactory);
          ????????//?將@Configuration類生成BeanDefinition,此時的BeanDefinition.beanClass為原類class對象
          ????????invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors,?registry);?//?(3)

          ????????//?Now,?invoke?the?postProcessBeanFactory?callback?of?all?processors?handled?so?far.
          ????????//?將(3)處的BeanDefinition.beanClass賦值為proxy代理類
          ????????nvokeBeanFactoryPostProcessors(registryProcessors,?beanFactory);(4)
          ????}
          ????···
          }

          這個方法處理所有的BeanFactoryPostProcessor類型對象,但是通過優(yōu)先級來規(guī)定先后處理順序,優(yōu)先級維度有兩個:1:BeanDefinitionRegistryPostProcessor類型和BeanFactoryPostProcessor類型;2:PriorityOrdered和Ordered。而
          ConfigurationClassPostProcessor同時實現(xiàn)了BeanDefinitionRegistryPostProcessor和PriorityOrdered,所以它得到最優(yōu)先處理的機會

          如上方法(1)處,從spring容器獲取BeanDefinitionRegistryPostProcessor類型的beanName, 這個時期只有ConfigurationClassPostProcessor的beanName滿足,需要指出:ConfigurationClassPostProcessor是通過硬編碼的方式注冊到spring容器的,詳見AnnotationConfigUtils類),接著執(zhí)行如上方法(2)處,以beanName為key從spring容器中獲取ConfigurationClassPostProcessor實例。

          應(yīng)用ConfigurationClassPostProcessor實例加載@Configuration進行BeanDefinition注冊

          如上方法(3)處,即為應(yīng)用ConfigurationClassPostProcessor實例。

          PostProcessorRegistrationDelegate?class
          private?static?void?invokeBeanDefinitionRegistryPostProcessors(
          ????????Collection?postProcessors,?BeanDefinitionRegistry?registry)?{
          ????for?(BeanDefinitionRegistryPostProcessor?postProcessor?:?postProcessors)?{
          ????????postProcessor.postProcessBeanDefinitionRegistry(registry);
          ????}
          }

          ConfigurationClassPostProcessor?class
          @Override
          public?void?postProcessBeanDefinitionRegistry(BeanDefinitionRegistry?registry)?{
          ????processConfigBeanDefinitions(registry);
          }

          從方法名大概就知道方法的作用了,會調(diào)用所有BeanDefinitionRegistryPostProcessor類型postProcessBeanDefinitionRegistry()方法,此處只有ConfigurationClassPostProcessor符合。

          應(yīng)用ConfigurationClassPostProcessor實例將BeanDefinition.beanClass生成proxy代理類

          如上invokeBeanFactoryPostProcessors方法(4)處,經(jīng)過了方法(3)的處理,@Configuration類已經(jīng)生成BeanDefinition,此時BeanDefinition.beanClass值為原類class對象。而方法(4)處目的是將BeanDefinition.beanClass賦值為proxy代理類,這里留個問題,其他為什么要設(shè)置成proxy代理類呢

          關(guān)于方法(3)和方法(4)要做的事,參考下圖,時機和作用清晰的對比和展示

          @Configuration class過程解析

          下面的方法就是解析@Configuration class的核心邏輯了。解析過程可以總結(jié)分三步,正好對應(yīng)著方法中(1),(2),(3)處

          ConfigurationClassPostProcessor?class
          public?void?processConfigBeanDefinitions(BeanDefinitionRegistry?registry)?
          {
          ????List?configCandidates?=?new?ArrayList<>();
          ????String[]?candidateNames?=?registry.getBeanDefinitionNames();?

          ????for?(String?beanName?:?candidateNames)?{
          ????????BeanDefinition?beanDef?=?registry.getBeanDefinition(beanName);
          ????????if?(ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef,?this.metadataReaderFactory)){
          ????????????configCandidates.add(new?BeanDefinitionHolder(beanDef,?beanName));//(1)
          ????????}
          ????}
          ????···?排序等
          s
          ????//?Parse?each?@Configuration?class
          ????ConfigurationClassParser?parser?=?new?ConfigurationClassParser(
          ????????????this.metadataReaderFactory,?this.problemReporter,?this.environment,
          ????????????this.resourceLoader,?this.componentScanBeanNameGenerator,?registry);

          ????Set?candidates?=?new?LinkedHashSet<>(configCandidates);
          ????Set?alreadyParsed?=?new?HashSet<>(configCandidates.size());
          ????do?{
          ????????parser.parse(candidates);?//(2)
          ????????parser.validate();

          ????????Set?configClasses?=?new?LinkedHashSet<>(parser.getConfigurationClasses());

          ????????//?Read?the?model?and?create?bean?definitions?based?on?its?content
          ????????this.reader?=?new?ConfigurationClassBeanDefinitionReader(
          ????????????????registry,?this.sourceExtractor,?this.resourceLoader,?this.environment,
          ????????????????this.importBeanNameGenerator,?parser.getImportRegistry());

          ????????this.reader.loadBeanDefinitions(configClasses);?//(3)

          ????}
          ????while?(!candidates.isEmpty());

          ????···?···
          }

          step1: 獲取候選者
          從spring容器拿到所有的beanDefinitionNames,然后遍歷驗證獲得候選者,驗證的依據(jù)是class metadata是否含有@Configuration注解,從下面我們可知,此時,beanDefinitionNames中只有
          consumerFeignApp符合條件。所以候選者就是consumerFeignApp及他的beanDefinition

          step2: 通過候選者獲取ConfigurationClass
          找到了候選者,下面就對候選者進行解析,解析的全部功能和邏輯都集中在ConfigurationClassParser類中,看名稱可知,這個類專業(yè)解析@Configuration類。

          ConfigurationClassParser?class
          public?void?parse(Set?configCandidates)?
          {
          ????for?(BeanDefinitionHolder?holder?:?configCandidates)?{
          ????????BeanDefinition?bd?=?holder.getBeanDefinition();
          ????????if?(bd?instanceof?AnnotatedBeanDefinition)?{
          ????????????parse(((AnnotatedBeanDefinition)?bd).getMetadata(),?holder.getBeanName());
          ????????}
          ????????...
          ????}

          ????this.deferredImportSelectorHandler.process();
          }

          protected?final?void?parse(AnnotationMetadata?metadata,?String?beanName)?throws?IOException?{
          ????processConfigurationClass(new?ConfigurationClass(metadata,?beanName));
          }

          protected?void?processConfigurationClass(ConfigurationClass?configClass)?throws?IOException?{
          ????//?評估標識@Configuration的類是否滿足條件去加載,這是條件注解@ConditionalXXX起的作用
          ????//?實際開發(fā)中,我們可以依據(jù)這個功能實現(xiàn)靈活的加載配置(如讓誰加載進來,不讓誰加載進來^_^)
          ????if?(this.conditionEvaluator.shouldSkip(configClass.getMetadata(),?ConfigurationPhase.PARSE_CONFIGURATION))?{
          ????????return;
          ????}

          ????//?Recursively?process?the?configuration?class?and?its?superclass?hierarchy.
          ????SourceClass?sourceClass?=?asSourceClass(configClass);
          ????do?{
          ????????sourceClass?=?doProcessConfigurationClass(configClass,?sourceClass);
          ????}
          ????while?(sourceClass?!=?null);

          ????//?所有加載的@Configuration類都會轉(zhuǎn)為ConfigurationClass放入這個map中
          ????this.configurationClasses.put(configClass,?configClass);
          }

          protected?final?SourceClass?doProcessConfigurationClass(ConfigurationClass?configClass,?SourceClass?sourceClass){
          ????//?為了集中說明意圖,隱藏了代碼
          ????//?Recursively?process?any?member?(nested)?classes?first
          ????//?Process?any?@PropertySource?annotations
          ????//?Process?any?@ComponentScan?annotations
          ????for?(AnnotationAttributes?componentScan?:?componentScans)?{
          ????????for?(BeanDefinitionHolder?holder?:?scannedBeanDefinitions)?{
          ????????????if?()?{
          ????????????????//?進入遞歸調(diào)用
          ????????????????parse(bdCand.getBeanClassName(),?holder.getBeanName());
          ????????????}
          ????????}
          ????}

          ????//?Process?any?@Import?annotations
          ????//?Process?any?@ImportResource?annotations
          ????//?Process?individual?@Bean?methods
          ????//?Process?default?methods?on?interfaces
          ????//?Process?superclass,?if?any
          ????//?No?superclass?->?processing?is?complete
          ????return?null;
          }

          如上方法整體的邏輯為對ConfigurationClass和SourceClass解析,檢查他們有沒有@ComponentScan,@Import,@Bean methods,@ImportResource,@PropertySource這些注解,如果有,分別對其解析,解析后的結(jié)果放入ConfigurationClass的各屬性中

          各個注解的屬性值中可能又包含@Configuration注解,又要對包含的@Configuration注解進行解析,這樣形成了遞歸,所以解析過程中有三個方法形成了三角遞歸調(diào)用的邏輯,

          這一步會將我們項目中定義的@Configuration類都加載進來,你可能有疑問,難道項目中我們自己定義的@Configuration類都是靠遞歸加載進來的?答案當然是NO,請注意@ComponentScan注解,這個注解的解析器很厲害,它把所有的標識@Component注解的class加載進來,而@Configuration,@RestController,@Service,@Repository等都包含@Component,所有這些注解的class都會加載進來形成BeanDefinition存入spring 容器(解析過程詳見ComponentScanAnnotationParser)。說回來,對于@ComponentScan解析器加載進來的BeanDefinitoin,會進行時@Configuration進行過濾,從而得到@Configuration類,再次調(diào)用parse()方法,這時體現(xiàn)出三角遞歸調(diào)用了。此時,項目中所有我們自定義的@Configuration類都獲取到了

          step3: 解析每個ConfigurationClass
          step2中對@Configuration類的@Import,@Bean methods,@ImportResource進行解析,解析的結(jié)果放入ConfigurationClass對象的importBeanDefinitionRegistrars,beanMethods,importedResources,metadata等屬性。
          所以,step2將@Configuration類的解析結(jié)果都放入了ConfigurationClass對象,即ConfigurationClass對象包裝了@Configuration類的所有信息。

          回到ConfigurationClassPostProcessor.processConfigBeanDefinitions()方法(3)處,現(xiàn)在,我們解析ConfigurationClass,而解析ConfigurationClass過程由ConfigurationClassBeanDefinitionReader類負責的
          看code

          ConfigurationClassBeanDefinitionReader?class
          public?void?loadBeanDefinitions(Set?configurationModel)?{
          ????TrackedConditionEvaluator?trackedConditionEvaluator?=?new?TrackedConditionEvaluator();
          ????for?(ConfigurationClass?configClass?:?configurationModel)?{
          ????????loadBeanDefinitionsForConfigurationClass(configClass,?trackedConditionEvaluator);
          ????}
          }

          private?void?loadBeanDefinitionsForConfigurationClass(
          ????????ConfigurationClass?configClass,?TrackedConditionEvaluator?trackedConditionEvaluator)?{

          ????if?(trackedConditionEvaluator.shouldSkip(configClass))?{
          ????????String?beanName?=?configClass.getBeanName();
          ????????if?(StringUtils.hasLength(beanName)?&&?this.registry.containsBeanDefinition(beanName))?{
          ????????????this.registry.removeBeanDefinition(beanName);
          ????????}
          ????????this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
          ????????return;
          ????}

          ????if?(configClass.isImported())?{
          ????????registerBeanDefinitionForImportedConfigurationClass(configClass);
          ????}
          ????for?(BeanMethod?beanMethod?:?configClass.getBeanMethods())?{
          ????????loadBeanDefinitionsForBeanMethod(beanMethod);
          ????}

          ????loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
          ????loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
          }

          看loadBeanDefinitionsForConfigurationClass()方法,方法主要功能為對ConfigurationClass的beanMethods,importedResources,importBeanDefinitionReistrars屬性進行解析,為什么要對這三個屬性進行解析呢,看看這三個其@Import,@Bean methods,@ImportResource的用法

          @Import(DispatcherServletConfiguration.class)
          @ImportResource("classpath:/com/acme/database-config.xml")
          protected?static?class?DispatcherServletRegistrationConfiguration?{

          ????@Bean(name?=?DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
          ????public?DispatcherServletRegistrationBean?dispatcherServletRegistration(){
          ????????...
          ????}
          }

          可以看到,這三個注解的屬性值都是類或者配置文件或者加載文件的類,所以,需要解析,從而將解析到的.class文件轉(zhuǎn)化為BeanDefinition放入spring容器。

          @Configuration類的cglib代理類實例化分析

          由于@Configuration注解的都是類,而非接口,所有這里使用的是cglib代理技術(shù),ConfigurationClassEnhancer包裝了cglib。這里我們實際工作中可以直接復(fù)用ConfigurationClassEnhancer滿足我們生成代理類的場景

          實現(xiàn)自BeanFactoryPostProcessor.postProcessBeanFactory
          ConfigurationClassPostProcessor?class
          @Override
          public?void?postProcessBeanFactory(ConfigurableListableBeanFactory?beanFactory)?{
          ????//?生成代理
          ????enhanceConfigurationClasses(beanFactory);
          ????beanFactory.addBeanPostProcessor(new?ImportAwareBeanPostProcessor(beanFactory));
          }

          ConfigurationClassPostProcessor.ConfigurationClassEnhancer?class
          public?void?enhanceConfigurationClasses(ConfigurableListableBeanFactory?beanFactory)?{
          ????Map?configBeanDefs?=?new?LinkedHashMap<>();
          ????for?(String?beanName?:?beanFactory.getBeanDefinitionNames())?{
          ????????BeanDefinition?beanDef?=?beanFactory.getBeanDefinition(beanName);
          ????????if?(ConfigurationClassUtils.isFullConfigurationClass(beanDef))?{
          ????????????if?(!(beanDef?instanceof?AbstractBeanDefinition))?{
          ????????????????throw?
          ????????????}
          ????????????configBeanDefs.put(beanName,?(AbstractBeanDefinition)?beanDef);
          ????????}
          ????}

          ????ConfigurationClassEnhancer?enhancer?=?new?ConfigurationClassEnhancer();
          ????for?(Map.Entry?entry?:?configBeanDefs.entrySet())?{
          ????????AbstractBeanDefinition?beanDef?=?entry.getValue();
          ????????//?If?a?@Configuration?class?gets?proxied,?always?proxy?the?target?class
          ????????beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE,?Boolean.TRUE);
          ????????//?Set?enhanced?subclass?of?the?user-specified?bean?class
          ????????Class?configClass?=?beanDef.resolveBeanClass(this.beanClassLoader);
          ????????if?(configClass?!=?null)?{
          ????????????Class?enhancedClass?=?enhancer.enhance(configClass,?this.beanClassLoader);
          ????????????if?(configClass?!=?enhancedClass)?{
          ????????????????beanDef.setBeanClass(enhancedClass);
          ????????????}
          ????????}
          ????}
          }

          ConfigurationClassPostProcessor.ConfigurationClassEnhancer?class
          public?Class?enhance(Class?configClass,?@Nullable?ClassLoader?classLoader)?{
          ????if?(EnhancedConfiguration.class.isAssignableFrom(configClass))?{
          ????????return?configClass;
          ????}
          ????Class?enhancedClass?=?createClass(newEnhancer(configClass,?classLoader));
          ????return?enhancedClass;
          }

          代理的代碼很清晰,很值得我們學習

          it`s time to summary

          整個過程可以看做是一顆小樹長成參天大樹,consumerFeignApp就是樹苗,而我們項目的代碼就是后來大樹的枝干和葉子。枝干和葉子與@ComponentScan,@Import,@Bean methods,@ImportResource,@PropertySource,@Configuration交織在一起被解析出來,生成beanDefinition、實例對象或代理類。

          本文主要說明了標識了@Configuration的.class文件,是如何被解析成ConfigurationClass,再到轉(zhuǎn)化為ConfigurationClassBeanDefinition放入spring容器,再到如何解析ConfigurationClass對象屬性。為了集中闡述@Configuration,所以,其他的部分這里不做詳述和延展。如果閱讀后你有所收獲,共享歡喜

          知識點收獲回復(fù)

          1,2,3,4相信你已經(jīng)有答案了,現(xiàn)在,我們看看擴展點有什么

          1. 加載一些package目錄下的.class文件

          ClassPathBeanDefinitionScanner?scanner?=?new?ClassPathBeanDefinitionScanner(this.registry,
          ????????????componentScan.getBoolean("useDefaultFilters"),?this.environment,?this.resourceLoader);
          Set?beanDefinitions?=?scanner.doScan(StringUtils.toStringArray(basePackages));
          如此兩行代碼就實現(xiàn)了加載package目錄下的.class文件的功能。詳見ComponentScanAnnotationParser.parse()方法


          source:?//yaoyuanyy.github.io/2019/12/12/springboot%20@Configuration類型的class需要知道的細節(jié)

          干貨分享

          最近將個人學習筆記整理成冊,使用PDF分享。關(guān)注我,回復(fù)如下代碼,即可獲得百度盤地址,無套路領(lǐng)取!

          ?001:《Java并發(fā)與高并發(fā)解決方案》學習筆記;?002:《深入JVM內(nèi)核——原理、診斷與優(yōu)化》學習筆記;?003:《Java面試寶典》?004:《Docker開源書》?005:《Kubernetes開源書》?006:《DDD速成(領(lǐng)域驅(qū)動設(shè)計速成)》?007:全部?008:加技術(shù)群討論

          加個關(guān)注不迷路

          喜歡就點個"在看"唄^_^

          瀏覽 37
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <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>
                  欧美性受XXXX黑人XYX性 | 日批网站全免费 | PP特极毛片 | 国产视频一二三 | 日韩黄色一级A片 |