Spring Boot 中 @EnableXXX 注解的驅動邏輯
點擊上方藍色“程序猿DD”,選擇“設為星標”
回復“資源”獲取獨家整理的學習資料!

作者 |?溫安適
工作中經常用到,如下注解:
@EnableEurekaClient @EnableFeignClients @EnableCircuitBreaker @EnableHystrix
他們都是@Enable開頭,各自實現不同的功能,解析這種@Enable的邏輯是什么呢?
@Enable驅動邏輯
找入口
@Enable的模塊驅動,依賴于@Import實現。
@Import作用是裝載導入類,主要包括@Configuration class,ImportSelector實現類,ImportBeanDefinitionRegistrar實現類。
XML時代,經常是@Import,,一起使用。
(注解配置)中大概率有我們需要找的邏輯。
根據 Spring Framework 2.0引入的可擴展的XML編程機制,XML Schema命名空間需要與Handler建立映射關系。
該關系配置在相對于classpath下的/META-INF/spring.handlers中。
查看ContextNamespaceHandler 源碼
public?class?ContextNamespaceHandler?extends?NamespaceHandlerSupport?{
???@Override
???public?void?init()?{
???????//省略其他代碼
????registerBeanDefinitionParser("annotation-config",?
????????????????????new?AnnotationConfigBeanDefinitionParser());
??}
}復制代碼
** 對應AnnotationConfigBeanDefinitionParser這個就是要找的入口**
找核心類
從AnnotationConfigBeanDefinitionParser的parse方法開始一路向下,找到
AnnotationConfigUtils.registerAnnotationConfigProcessors中注冊了ConfigurationClassPostProcessor。

ConfigurationClassPostProcessor類注釋說明
\1. 用于的引導處理@Configuration類
\2. context:annotation-config/或 context:component-scan/時會注冊
否則需要手工編程
\3. ConfigurationClassPostProcessor第一優(yōu)先級,保證
@Configuration}類中聲明@Bean,在其他 BeanFactoryPostProcessor執(zhí)行之前被注冊
擴展
AnnotationConfigApplicationContext中new AnnotationBeanDefinitionReader也調用了 AnnotationConfigUtils .
registerAnnotationConfigProcessors
從類注釋中,可以看出ConfigurationClassPostProcessor就是要找的核心類
找核心方法
查看 ConfigurationClassPostProcessor 的層級關系為

Aware系列注入相應資源,Ordered設置優(yōu)先級,值得關注的就是
postProcessBeanDefinitionRegistry了。
postProcessBeanDefinitionRegistry其內部有2個方法
postProcessBeanDefinitionRegistry在BeanDefinition注冊之后,BeanFactoryPostProcessor執(zhí)行之前,修改或重寫B(tài)eanDefinition 繼承自BeanFactoryPostProcessor的postProcessBeanFactory,BeanDefinition加載之后,Bean實例化之前,重寫或添加BeanDefinition,修改BeanFactory
瀏覽2個方法,都有processConfigBeanDefinitions,從名稱可以看出是處理配置類Bean定義

ConfigurationClassPostProcessor#processConfigBeanDefinitions就是要找的核心方法
梳理流程
public?void?processConfigBeanDefinitions(BeanDefinitionRegistry?registry)?{
???List?configCandidates?=?new?ArrayList<>();
???String[]?candidateNames?=?registry.getBeanDefinitionNames();
???for?(String?beanName?:?candidateNames)?{
??????BeanDefinition?beanDef?=?registry.getBeanDefinition(beanName);
??????if?(ConfigurationClassUtils.isFullConfigurationClass(beanDef)?||
????????????ConfigurationClassUtils.isLiteConfigurationClass(beanDef))?{
?????????if?(logger.isDebugEnabled())?{
????????????logger.debug("Bean?definition?has?already?been?processed?as?a?configuration?class:?"?+?beanDef);
?????????}
??????}
??????else?if?(ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef,?this.metadataReaderFactory))?{
?????????configCandidates.add(new?BeanDefinitionHolder(beanDef,?beanName));
??????}
???}
???//?沒有找到?@Configuration?classes?立即返回
???if?(configCandidates.isEmpty())?{
??????return;
???}
???//根據@Order?值進行排序
???configCandidates.sort((bd1,?bd2)?->?{
??????int?i1?=?ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
??????int?i2?=?ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
??????return?Integer.compare(i1,?i2);
???});
???//通過封閉的應用程序上下文,?檢測任何自定義bean名稱生成策略
???supplied?through?the?enclosing?application?context
???SingletonBeanRegistry?sbr?=?null;
???if?(registry?instanceof?SingletonBeanRegistry)?{
??????sbr?=?(SingletonBeanRegistry)?registry;
??????if?(!this.localBeanNameGeneratorSet)?{
?????????BeanNameGenerator?generator?=?(BeanNameGenerator)?sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
?????????if?(generator?!=?null)?{
????????????this.componentScanBeanNameGenerator?=?generator;
????????????this.importBeanNameGenerator?=?generator;
?????????}
??????}
???}
???if?(this.environment?==?null)?{
??????this.environment?=?new?StandardEnvironment();
???}
???//?解析@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);
??????parser.validate();
??????Set?configClasses?=?new?LinkedHashSet<>(parser.getConfigurationClasses());
??????configClasses.removeAll(alreadyParsed);
??????//?讀ConfigurationClass的信息,創(chuàng)建BeanDefinition
??????if?(this.reader?==?null)?{
?????????this.reader?=?new?ConfigurationClassBeanDefinitionReader(
???????????????registry,?this.sourceExtractor,?this.resourceLoader,?this.environment,
???????????????this.importBeanNameGenerator,?parser.getImportRegistry());
??????}
??????this.reader.loadBeanDefinitions(configClasses);
??????alreadyParsed.addAll(configClasses);
??????candidates.clear();
??????if?(registry.getBeanDefinitionCount()?>?candidateNames.length)?{
?????????String[]?newCandidateNames?=?registry.getBeanDefinitionNames();
?????????Set?oldCandidateNames?=?new?HashSet<>(Arrays.asList(candidateNames));
?????????Set?alreadyParsedClasses?=?new?HashSet<>();
?????????for?(ConfigurationClass?configurationClass?:?alreadyParsed)?{
????????????alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
?????????}
?????????for?(String?candidateName?:?newCandidateNames)?{
????????????if?(!oldCandidateNames.contains(candidateName))?{
???????????????BeanDefinition?bd?=?registry.getBeanDefinition(candidateName);
???????????????if?(ConfigurationClassUtils.checkConfigurationClassCandidate(bd,?this.metadataReaderFactory)?&&
?????????????????????!alreadyParsedClasses.contains(bd.getBeanClassName()))?{
??????????????????candidates.add(new?BeanDefinitionHolder(bd,?candidateName));
???????????????}
????????????}
?????????}
?????????candidateNames?=?newCandidateNames;
??????}
???}
???while?(!candidates.isEmpty());
???//?將ImportRegistry注冊為bean以支持importware@Configuration類
???if?(sbr?!=?null?&&?!sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME))?{
??????sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME,?parser.getImportRegistry());
???}
???if?(this.metadataReaderFactory?instanceof?CachingMetadataReaderFactory)?{
??????//?Clear?cache?in?externally?provided?MetadataReaderFactory;?this?is?a?no-op
??????//?for?a?shared?cache?since?it'll?be?cleared?by?the?ApplicationContext.
??????((CachingMetadataReaderFactory)?this.metadataReaderFactory).clearCache();
???}
}復制代碼
ConfigurationClassPostProcessor#processConfigBeanDefinitions核心如下:
根據@Order 值進行排序 解析@Configuration class 為ConfigurationClass對象 讀ConfigurationClass的信息,創(chuàng)建BeanDefinition 將ImportRegistry注冊為bean以支持importware@Configuration類
重點關注解析方法
ConfigurationClassParser#parse方法負責解析@Configuration class 為ConfigurationClass對象
查閱其源碼如下:

ConfigurationClassParser#doProcessConfigurationClass代碼如下:
protected?final?SourceClass?doProcessConfigurationClass(ConfigurationClass?configClass,?SourceClass?sourceClass)
??????throws?IOException?{
???if?(configClass.getMetadata().isAnnotated(Component.class.getName()))?{
??????//?Recursively?process?any?member?(nested)?classes?first
??????processMemberClasses(configClass,?sourceClass);
???}
???//?Process?any?@PropertySource?annotations
???for?(AnnotationAttributes?propertySource?:?AnnotationConfigUtils.attributesForRepeatable(
?????????sourceClass.getMetadata(),?PropertySources.class,
?????????org.springframework.context.annotation.PropertySource.class))?{
??????if?(this.environment?instanceof?ConfigurableEnvironment)?{
?????????processPropertySource(propertySource);
??????}
??????else?{
?????????logger.info("Ignoring?@PropertySource?annotation?on?["?+?sourceClass.getMetadata().getClassName()?+
???????????????"].?Reason:?Environment?must?implement?ConfigurableEnvironment");
??????}
???}
???//?Process?any?@ComponentScan?annotations
???Set?componentScans?=?AnnotationConfigUtils.attributesForRepeatable(
?????????sourceClass.getMetadata(),?ComponentScans.class,?ComponentScan.class);
???if?(!componentScans.isEmpty()?&&
?????????!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(),?ConfigurationPhase.REGISTER_BEAN))?{
??????for?(AnnotationAttributes?componentScan?:?componentScans)?{
?????????//?The?config?class?is?annotated?with?@ComponentScan?->?perform?the?scan?immediately
?????????Set?scannedBeanDefinitions?=
???????????????this.componentScanParser.parse(componentScan,?sourceClass.getMetadata().getClassName());
?????????//?Check?the?set?of?scanned?definitions?for?any?further?config?classes?and?parse?recursively?if?needed
?????????for?(BeanDefinitionHolder?holder?:?scannedBeanDefinitions)?{
????????????BeanDefinition?bdCand?=?holder.getBeanDefinition().getOriginatingBeanDefinition();
????????????if?(bdCand?==?null)?{
???????????????bdCand?=?holder.getBeanDefinition();
????????????}
????????????if?(ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand,?this.metadataReaderFactory))?{
???????????????parse(bdCand.getBeanClassName(),?holder.getBeanName());
????????????}
?????????}
??????}
???}
???//?Process?any?@Import?annotations
???processImports(configClass,?sourceClass,?getImports(sourceClass),?true);
???//?Process?any?@ImportResource?annotations
???AnnotationAttributes?importResource?=
?????????AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(),?ImportResource.class);
???if?(importResource?!=?null)?{
??????String[]?resources?=?importResource.getStringArray("locations");
??????Class?extends?BeanDefinitionReader>?readerClass?=?importResource.getClass("reader");
??????for?(String?resource?:?resources)?{
?????????String?resolvedResource?=?this.environment.resolveRequiredPlaceholders(resource);
?????????configClass.addImportedResource(resolvedResource,?readerClass);
??????}
???}
???//?Process?individual?@Bean?methods
???Set?beanMethods?=?retrieveBeanMethodMetadata(sourceClass);
???for?(MethodMetadata?methodMetadata?:?beanMethods)?{
??????configClass.addBeanMethod(new?BeanMethod(methodMetadata,?configClass));
???}
???//?Process?default?methods?on?interfaces
???processInterfaces(configClass,?sourceClass);
???//?Process?superclass,?if?any
???if?(sourceClass.getMetadata().hasSuperClass())?{
??????String?superclass?=?sourceClass.getMetadata().getSuperClassName();
??????if?(superclass?!=?null?&&?!superclass.startsWith("java")?&&
????????????!this.knownSuperclasses.containsKey(superclass))?{
?????????this.knownSuperclasses.put(superclass,?configClass);
?????????//?Superclass?found,?return?its?annotation?metadata?and?recurse
?????????return?sourceClass.getSuperClass();
??????}
???}
???//?No?superclass?->?processing?is?complete
???return?null;
}復制代碼
ConfigurationClassParser#doProcessConfigurationClass(ConfigurationClass,AnnatationMetatdata)將@PropertySource, ? @ComponentScan, @Import,@ImportResource,@Bean等一起處理了。
看到這里基本邏輯已經理清 了,但是有一個疑問
@Configuration中的@Bean沒有其他特殊處理嗎?
瀏覽代碼解決疑問

從上邊瀏覽的代碼可以看到完全模式,會被AOP增強
那什么是完全模式呢?在ConfigurationClassUtils找到如下方法:
public?class?ConfigurationClassUtils{
//省略其他方法
public?static?boolean?isFullConfigurationCandidate(AnnotationMetadata?metadata)?{
???return?metadata.isAnnotated(Configuration.class.getName());
}
}復制代碼
即 @Configuration class是完全模式,@Component,@Bean是輕量級模式
那AOP增強了作用是什么呢?查看 ConfigurationClassEnhancer 的類注釋如下:
/**
* Enhances {@link
Configuration
} classes by generating a CGLIB subclass which
* interacts with the Spring container to respect bean scoping semantics for
* {@code @Bean} methods. Each such {@code @Bean} method will be overridden in
* the generated subclass, only delegating to the actual {@code @Bean} method
* implementation if the container actually requests the construction of a new
* instance. Otherwise, a call to such an {@code @Bean} method serves as a
* reference back to the container, obtaining the corresponding bean by name.
* @author Chris Beams
* @author Juergen Hoeller
* @since 3.0
* @see #enhance
* @see
ConfigurationClassPostProcessor
*/
class ?ConfigurationClassEnhancer ?{
大概意思如下:
通過CGLIB增強@Configuration class。
每個@Bean方法會生成子類。
首次被調用時,@Bean方法會被執(zhí)行用于創(chuàng)建bean實例;
再次被調用時,不會再執(zhí)行創(chuàng)建bean實例,而是根據bean名稱返回首次該方法被執(zhí)行時創(chuàng)建的bean實例。
總結
1.ConfigurationClassPostProcessor負責篩選@Component Class、@Configuration Class以及@Bean定義的Bean,**
2.ConfigurationClassParser從候選的Bean定義中解析出ConfigurationClass集合,隨后被3.ConfigurationClassBeanDefinitionReader轉換為BeanDefinition
4.ConfigurationClassParser的解析順序,
@PropertySource->@ComponentScan->@Import->@ImportResource->@Bean->接口的默認方法->處理父類
5.@Configuration class是完全模式,@Component,@Bean是輕量級模式
6.CGLIB增強@Configuration class。每個@Bean方法會生成子類。
首次被調用時,@Bean方法會被執(zhí)行用于創(chuàng)建bean實例;
再次被調用時,不會再執(zhí)行創(chuàng)建bean實例,而是根據bean名稱返回首次該方法被執(zhí)行時創(chuàng)建的bean實例。
往期推薦
掃一掃,關注我
一起學習,一起進步
