@Configuration 類型的 class 需要知道的細(xì)節(jié)
知識點(diǎn)收獲
通過本文,你能收獲什么?
@Configuration.class文件如何被spring加載的?
@Configuration.class文件如何被轉(zhuǎn)化為BeanDefinition放入spring容器的?
@Configuration的BeanDefinition如何轉(zhuǎn)化為ConfigurationClass對象的
ConfigurationClass對象屬性在哪里開始被解析的?
@Configuration的BeanDefinition的beanClass值何時(shí)變成proxy代理類的,為什么要變
@Configuration類最終會生成cglib proxy代理類,這個(gè)由@Configuration類生成的cglib proxy代理類如何實(shí)例化的
我們能從中得到的擴(kuò)展點(diǎn)有哪些
@Configuration注解作用
@Configuration標(biāo)識的類有這些特性:可以聲明多個(gè)@Bean方法,且在運(yùn)行時(shí)被spring容器處理來生成BeanDefinition。@Configuration類是被AnnotationConfigWebApplicationContext啟動(dòng)(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é)作,從加載標(biāo)識@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實(shí)例創(chuàng)建過程
我們剛說過,ConfigurationClassPostProcessor是處理@Configuration class的核心組件,它是BeanFactoryPostProcessor類型子類且是BeanDefinitoinRegistryPostProcessor類型子類。BeanFactoryPostProcessor是AbstractApplicationContext's post-processor handling技術(shù)的規(guī)范接口,在項(xiàng)目啟動(dòng)較早時(shí)段,它便開始工作?;蛘哒f任何spring boot項(xiàng)目啟動(dòng)時(shí)都是走post-processor handling處理邏輯,這個(gè)邏輯的入口在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,此時(shí)的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)
????}
????···
}
這個(gè)方法處理所有的BeanFactoryPostProcessor類型對象,但是通過優(yōu)先級來規(guī)定先后處理順序,優(yōu)先級維度有兩個(gè):1:BeanDefinitionRegistryPostProcessor類型和BeanFactoryPostProcessor類型;2:PriorityOrdered和Ordered。而
ConfigurationClassPostProcessor同時(shí)實(shí)現(xiàn)了BeanDefinitionRegistryPostProcessor和PriorityOrdered,所以它得到最優(yōu)先處理的機(jī)會
如上方法(1)處,從spring容器獲取BeanDefinitionRegistryPostProcessor類型的beanName, 這個(gè)時(shí)期只有ConfigurationClassPostProcessor的beanName滿足,需要指出:ConfigurationClassPostProcessor是通過硬編碼的方式注冊到spring容器的,詳見AnnotationConfigUtils類),接著執(zhí)行如上方法(2)處,以beanName為key從spring容器中獲取ConfigurationClassPostProcessor實(shí)例。
應(yīng)用ConfigurationClassPostProcessor實(shí)例加載@Configuration進(jìn)行BeanDefinition注冊
如上方法(3)處,即為應(yīng)用ConfigurationClassPostProcessor實(shí)例。
PostProcessorRegistrationDelegate?class
private?static?void?invokeBeanDefinitionRegistryPostProcessors(
????????Collection?extends?BeanDefinitionRegistryPostProcessor>?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實(shí)例將BeanDefinition.beanClass生成proxy代理類
如上invokeBeanFactoryPostProcessors方法(4)處,經(jīng)過了方法(3)的處理,@Configuration類已經(jīng)生成BeanDefinition,此時(shí)BeanDefinition.beanClass值為原類class對象。而方法(4)處目的是將BeanDefinition.beanClass賦值為proxy代理類,這里留個(gè)問題,其他為什么要設(shè)置成proxy代理類呢
關(guān)于方法(3)和方法(4)要做的事,參考下圖,時(shí)機(jī)和作用清晰的對比和展示
@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,然后遍歷驗(yàn)證獲得候選者,驗(yàn)證的依據(jù)是class metadata是否含有@Configuration注解,從下面我們可知,此時(shí),beanDefinitionNames中只有consumerFeignApp符合條件。所以候選者就是consumerFeignApp及他的beanDefinition
step2: 通過候選者獲取ConfigurationClass
找到了候選者,下面就對候選者進(jìn)行解析,解析的全部功能和邏輯都集中在ConfigurationClassParser類中,看名稱可知,這個(gè)類專業(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?{
????//?評估標(biāo)識@Configuration的類是否滿足條件去加載,這是條件注解@ConditionalXXX起的作用
????//?實(shí)際開發(fā)中,我們可以依據(jù)這個(gè)功能實(shí)現(xiàn)靈活的加載配置(如讓誰加載進(jìn)來,不讓誰加載進(jì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放入這個(gè)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?()?{
????????????????//?進(jìn)入遞歸調(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的各屬性中
各個(gè)注解的屬性值中可能又包含@Configuration注解,又要對包含的@Configuration注解進(jìn)行解析,這樣形成了遞歸,所以解析過程中有三個(gè)方法形成了三角遞歸調(diào)用的邏輯,
這一步會將我們項(xiàng)目中定義的@Configuration類都加載進(jìn)來,你可能有疑問,難道項(xiàng)目中我們自己定義的@Configuration類都是靠遞歸加載進(jìn)來的?答案當(dāng)然是NO,請注意@ComponentScan注解,這個(gè)注解的解析器很厲害,它把所有的標(biāo)識@Component注解的class加載進(jìn)來,而@Configuration,@RestController,@Service,@Repository等都包含@Component,所有這些注解的class都會加載進(jìn)來形成BeanDefinition存入spring 容器(解析過程詳見ComponentScanAnnotationParser)。說回來,對于@ComponentScan解析器加載進(jìn)來的BeanDefinitoin,會進(jìn)行時(shí)@Configuration進(jìn)行過濾,從而得到@Configuration類,再次調(diào)用parse()方法,這時(shí)體現(xiàn)出三角遞歸調(diào)用了。此時(shí),項(xiàng)目中所有我們自定義的@Configuration類都獲取到了
step3: 解析每個(gè)ConfigurationClass
step2中對@Configuration類的@Import,@Bean methods,@ImportResource進(jìn)行解析,解析的結(jié)果放入ConfigurationClass對象的importBeanDefinitionRegistrars,beanMethods,importedResources,metadata等屬性。
所以,step2將@Configuration類的解析結(jié)果都放入了ConfigurationClass對象,即ConfigurationClass對象包裝了@Configuration類的所有信息。
回到ConfigurationClassPostProcessor.processConfigBeanDefinitions()方法(3)處,現(xiàn)在,我們解析ConfigurationClass,而解析ConfigurationClass過程由ConfigurationClassBeanDefinitionReader類負(fù)責(zé)的
看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屬性進(jìn)行解析,為什么要對這三個(gè)屬性進(jìn)行解析呢,看看這三個(gè)其@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(){
????????...
????}
}
可以看到,這三個(gè)注解的屬性值都是類或者配置文件或者加載文件的類,所以,需要解析,從而將解析到的.class文件轉(zhuǎn)化為BeanDefinition放入spring容器。
@Configuration類的cglib代理類實(shí)例化分析
由于@Configuration注解的都是類,而非接口,所有這里使用的是cglib代理技術(shù),ConfigurationClassEnhancer包裝了cglib。這里我們實(shí)際工作中可以直接復(fù)用ConfigurationClassEnhancer滿足我們生成代理類的場景
實(shí)現(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;
}
代理的代碼很清晰,很值得我們學(xué)習(xí)
it`s time to summary
整個(gè)過程可以看做是一顆小樹長成參天大樹,consumerFeignApp就是樹苗,而我們項(xiàng)目的代碼就是后來大樹的枝干和葉子。枝干和葉子與@ComponentScan,@Import,@Bean methods,@ImportResource,@PropertySource,@Configuration交織在一起被解析出來,生成beanDefinition、實(shí)例對象或代理類。
本文主要說明了標(biāo)識了@Configuration的.class文件,是如何被解析成ConfigurationClass,再到轉(zhuǎn)化為ConfigurationClassBeanDefinition放入spring容器,再到如何解析ConfigurationClass對象屬性。為了集中闡述@Configuration,所以,其他的部分這里不做詳述和延展。如果閱讀后你有所收獲,共享歡喜
知識點(diǎn)收獲回復(fù)
1,2,3,4相信你已經(jīng)有答案了,現(xiàn)在,我們看看擴(kuò)展點(diǎn)有什么
加載一些package目錄下的.class文件
ClassPathBeanDefinitionScanner?scanner?=?new?ClassPathBeanDefinitionScanner(this.registry,
????????????componentScan.getBoolean("useDefaultFilters"),?this.environment,?this.resourceLoader);
Set?beanDefinitions?=?scanner.doScan(StringUtils.toStringArray(basePackages));
如此兩行代碼就實(shí)現(xiàn)了加載package目錄下的.class文件的功能。詳見ComponentScanAnnotationParser.parse()方法 source:?//yaoyuanyy.github.io/2019/12/12/springboot%20@Configuration類型的class需要知道的細(xì)節(jié)

喜歡,在看
