<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 Boot 中 @EnableXXX 注解的驅(qū)動(dòng)邏輯

          共 9232字,需瀏覽 19分鐘

           ·

          2020-08-01 06:34

          回復(fù)“1024”獲取?2000+?道互聯(lián)網(wǎng)大廠面試題


          作者 |?溫安適

          來(lái)源 |?https://juejin.im/post/5efdd689e51d4534af686ca9

          工作中經(jīng)常用到,如下注解:

          • @EnableEurekaClient
          • @EnableFeignClients
          • @EnableCircuitBreaker
          • @EnableHystrix

          他們都是@Enable開(kāi)頭,各自實(shí)現(xiàn)不同的功能,解析這種@Enable的邏輯是什么呢?

          @Enable驅(qū)動(dòng)邏輯

          找入口

          @Enable的模塊驅(qū)動(dòng),依賴于@Import實(shí)現(xiàn)。

          @Import作用是裝載導(dǎo)入類,主要包括@Configuration class,ImportSelector實(shí)現(xiàn)類,ImportBeanDefinitionRegistrar實(shí)現(xiàn)類。

          XML時(shí)代,經(jīng)常是@Import,,一起使用。

          (注解配置)中大概率有我們需要找的邏輯。

          根據(jù) Spring Framework 2.0引入的可擴(kuò)展的XML編程機(jī)制,XML Schema命名空間需要與Handler建立映射關(guān)系。

          該關(guān)系配置在相對(duì)于classpath下的/META-INF/spring.handlers中。

          查看ContextNamespaceHandler 源碼

          public?class?ContextNamespaceHandler?extends?NamespaceHandlerSupport?{
          ???@Override
          ???public?void?init()?{
          ???????//省略其他代碼
          ????registerBeanDefinitionParser("annotation-config",?
          ????????????????????new?AnnotationConfigBeanDefinitionParser());
          ??}
          }復(fù)制代碼


          ** 對(duì)應(yīng)AnnotationConfigBeanDefinitionParser這個(gè)就是要找的入口**

          找核心類

          從AnnotationConfigBeanDefinitionParser的parse方法開(kāi)始一路向下,找到

          AnnotationConfigUtils.registerAnnotationConfigProcessors中注冊(cè)了ConfigurationClassPostProcessor。

          img

          ConfigurationClassPostProcessor類注釋說(shuō)明

          \1. 用于的引導(dǎo)處理@Configuration類

          \2. context:annotation-config/或 context:component-scan/時(shí)會(huì)注冊(cè)

          否則需要手工編程

          \3. ConfigurationClassPostProcessor第一優(yōu)先級(jí),保證

          @Configuration}類中聲明@Bean,在其他 BeanFactoryPostProcessor執(zhí)行之前被注冊(cè)

          擴(kuò)展

          AnnotationConfigApplicationContext中new AnnotationBeanDefinitionReader也調(diào)用了?AnnotationConfigUtils .

          registerAnnotationConfigProcessors

          從類注釋中,可以看出ConfigurationClassPostProcessor就是要找的核心類

          找核心方法

          查看 ConfigurationClassPostProcessor 的層級(jí)關(guān)系為

          img

          Aware系列注入相應(yīng)資源,Ordered設(shè)置優(yōu)先級(jí),值得關(guān)注的就是

          postProcessBeanDefinitionRegistry了。

          postProcessBeanDefinitionRegistry其內(nèi)部有2個(gè)方法

          1. postProcessBeanDefinitionRegistry在BeanDefinition注冊(cè)之后,BeanFactoryPostProcessor執(zhí)行之前,修改或重寫B(tài)eanDefinition
          2. 繼承自BeanFactoryPostProcessor的postProcessBeanFactory,BeanDefinition加載之后,Bean實(shí)例化之前,重寫或添加BeanDefinition,修改BeanFactory

          瀏覽2個(gè)方法,都有processConfigBeanDefinitions,從名稱可以看出是處理配置類Bean定義

          img

          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));
          ??????}
          ???}

          ???//?沒(méi)有找到?@Configuration?classes?立即返回
          ???if?(configCandidates.isEmpty())?{
          ??????return;
          ???}
          ???//根據(jù)@Order?值進(jìn)行排序
          ???configCandidates.sort((bd1,?bd2)?->?{
          ??????int?i1?=?ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
          ??????int?i2?=?ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
          ??????return?Integer.compare(i1,?i2);
          ???});
          ???//通過(guò)封閉的應(yīng)用程序上下文,?檢測(cè)任何自定義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注冊(cè)為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();
          ???}
          }復(fù)制代碼


          ConfigurationClassPostProcessor#processConfigBeanDefinitions核心如下:

          1. 根據(jù)@Order 值進(jìn)行排序
          2. 解析@Configuration class 為ConfigurationClass對(duì)象
          3. 讀ConfigurationClass的信息,創(chuàng)建BeanDefinition
          4. 將ImportRegistry注冊(cè)為bean以支持importware@Configuration類

          重點(diǎn)關(guān)注解析方法

          ConfigurationClassParser#parse方法負(fù)責(zé)解析@Configuration class 為ConfigurationClass對(duì)象

          查閱其源碼如下:

          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?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;
          }復(fù)制代碼


          ConfigurationClassParser#doProcessConfigurationClass(ConfigurationClass,AnnatationMetatdata)將@PropertySource, ??@ComponentScan,?@Import,@ImportResource,@Bean等一起處理了。

          看到這里基本邏輯已經(jīng)理清 了,但是有一個(gè)疑問(wèn)

          @Configuration中的@Bean沒(méi)有其他特殊處理嗎?

          瀏覽代碼解決疑問(wèn)

          ![img](data:image/svg+xml;utf8,)

          從上邊瀏覽的代碼可以看到完全模式,會(huì)被AOP增強(qiáng)

          那什么是完全模式呢?在ConfigurationClassUtils找到如下方法:

          public?class?ConfigurationClassUtils{
          //省略其他方法
          public?static?boolean?isFullConfigurationCandidate(AnnotationMetadata?metadata)?{
          ???return?metadata.isAnnotated(Configuration.class.getName());
          }
          }復(fù)制代碼


          即?@Configuration class是完全模式,@Component,@Bean是輕量級(jí)模式

          那AOP增強(qiáng)了作用是什么呢?查看 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 ?{

          大概意思如下:

          通過(guò)CGLIB增強(qiáng)@Configuration class。

          每個(gè)@Bean方法會(huì)生成子類。

          首次被調(diào)用時(shí),@Bean方法會(huì)被執(zhí)行用于創(chuàng)建bean實(shí)例;

          再次被調(diào)用時(shí),不會(huì)再執(zhí)行創(chuàng)建bean實(shí)例,而是根據(jù)bean名稱返回首次該方法被執(zhí)行時(shí)創(chuàng)建的bean實(shí)例。

          總結(jié)

          1.ConfigurationClassPostProcessor負(fù)責(zé)篩選@Component Class、@Configuration Class以及@Bean定義的Bean,**

          2.ConfigurationClassParser從候選的Bean定義中解析出ConfigurationClass集合,隨后被3.ConfigurationClassBeanDefinitionReader轉(zhuǎn)換為BeanDefinition

          4.ConfigurationClassParser的解析順序,

          @PropertySource->@ComponentScan->@Import->@ImportResource->@Bean->接口的默認(rèn)方法->處理父類

          5.@Configuration class是完全模式,@Component,@Bean是輕量級(jí)模式

          6.CGLIB增強(qiáng)@Configuration class。每個(gè)@Bean方法會(huì)生成子類。

          首次被調(diào)用時(shí),@Bean方法會(huì)被執(zhí)行用于創(chuàng)建bean實(shí)例;

          再次被調(diào)用時(shí),不會(huì)再執(zhí)行創(chuàng)建bean實(shí)例,而是根據(jù)bean名稱返回首次該方法被執(zhí)行時(shí)創(chuàng)建的bean實(shí)例。


          END



          免費(fèi)領(lǐng)取 1000+ 道面試資料!!小編這里有一份面試寶典《Java 核心知識(shí)點(diǎn).pdf》,覆蓋了 JVM,鎖、高并發(fā)、Spring原理、微服務(wù)、數(shù)據(jù)庫(kù)、Zookeep人、數(shù)據(jù)結(jié)構(gòu)等等知識(shí)點(diǎn),包含 Java 后端知識(shí)點(diǎn) 1000+ 個(gè),部分如下:

          如何獲取?加小編微信,回復(fù)【1024】

          瀏覽 33
          點(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>
                  成人免费 做爱视频 | 国内精品在线观看小视频 | 夜夜躁婷婷 | 好看的印度三色电费 | 青青草亚洲|