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

        共 5128字,需瀏覽 11分鐘

         ·

        2021-12-29 15:56

        知識點收獲

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

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

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

        • @Configuration的BeanDefinition如何轉化為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相關的類

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

        這些類分工協作,從加載標識@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技術的規(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同時實現了BeanDefinitionRegistryPostProcessor和PriorityOrdered,所以它得到最優(yōu)先處理的機會

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

        應用ConfigurationClassPostProcessor實例加載@Configuration進行BeanDefinition注冊

        如上方法(3)處,即為應用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);
        }

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

        應用ConfigurationClassPostProcessor實例將BeanDefinition.beanClass生成proxy代理類

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

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

        @Configuration class過程解析

        下面的方法就是解析@Configuration class的核心邏輯了。解析過程可以總結分三步,正好對應著方法中(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,然后遍歷驗證獲得候選者,驗證的依據是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ā)中,我們可以依據這個功能實現靈活的加載配置(如讓誰加載進來,不讓誰加載進來^_^)
        ????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類都會轉為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?()?{
        ????????????????//?進入遞歸調用
        ????????????????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這些注解,如果有,分別對其解析,解析后的結果放入ConfigurationClass的各屬性中

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

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

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

        回到ConfigurationClassPostProcessor.processConfigBeanDefinitions()方法(3)處,現在,我們解析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文件轉化為BeanDefinition放入spring容器。

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

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

        實現自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,再到轉化為ConfigurationClassBeanDefinition放入spring容器,再到如何解析ConfigurationClass對象屬性。為了集中闡述@Configuration,所以,其他的部分這里不做詳述和延展。如果閱讀后你有所收獲,共享歡喜

        知識點收獲回復

        1,2,3,4相信你已經有答案了,現在,我們看看擴展點有什么

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

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


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


        喜歡,在看

        瀏覽 49
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

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

        手機掃一掃分享

        分享
        舉報
          
          

            1. 欧美美女被日 | 三级黄色片网站 | 青青色在线视频 | 欧美色图视频在线观看 | 国产中文字幕在线观看 |