Spring boot的啟動加載原理


因為想要學習Spring cloud,所以需要學習Spring boot。對於Spring boot主要有以下兩點理解:

    1.起步依賴

    就好比,你要老媽子給你介紹,你只要關注介紹的這個人就好,至於老媽子怎么去托關系找你二大姑啊,三大姨來張羅啊,你都可以不用管了。

    相當於是,對於你需要的應用,它架包的傳遞依賴以及兼容性,spring boot都幫你做了,你無需再去各種架包引用,還得看是否兼容,大大提升了開發效率。

    2.自動配置

    自動配置,主要看classpath有沒有要初始的bean,會自動進行配置,也可以覆蓋自動配置,這里主要使用了spring的條件化配置。

 

    下圖是SpringApplication啟動原理:

       

 

                                                 圖1

 

  自動配置

       @SpringBootApplication主要涉及到以下三個注解:

  @Configuration

  @ComponentScan

  @EnableAutoConfiguration(最重要)

 

  Auto configure的加載:

  @EnableAutoConfiguration --> @Import(導入自動配置) -->@EnableAutoConfigurationImportSelector

                            (由SpringFactoriesLoader.loadFactoryNames加載EnableAutoConfiguration對應的自動配置項)

  @Configuration標記的類加載原理:

  configuration類的加載主要是ConfigurationClassPostProcessor類的processConfigBeanDefinitions方法

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
        List<BeanDefinitionHolder> configCandidates = new ArrayList();
        String[] candidateNames = registry.getBeanDefinitionNames();
        String[] var4 = candidateNames;
        int var5 = candidateNames.length;

        for(int var6 = 0; var6 < var5; ++var6) {
            String beanName = var4[var6];
            BeanDefinition beanDef = registry.getBeanDefinition(beanName);
            if(!ConfigurationClassUtils.isFullConfigurationClass(beanDef) && !ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
                if(ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
                    configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
                }
            } else if(this.logger.isDebugEnabled()) {
                this.logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
            }
        }

        if(!configCandidates.isEmpty()) {
            Collections.sort(configCandidates, new Comparator<BeanDefinitionHolder>() {
                public int compare(BeanDefinitionHolder bd1, BeanDefinitionHolder bd2) {
                    int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
                    int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
                    return i1 < i2?-1:(i1 > i2?1:0);
                }
            });
            SingletonBeanRegistry sbr = null;
            if(registry instanceof SingletonBeanRegistry) {
                sbr = (SingletonBeanRegistry)registry;
                if(!this.localBeanNameGeneratorSet && sbr.containsSingleton("org.springframework.context.annotation.internalConfigurationBeanNameGenerator")) {
                    BeanNameGenerator generator = (BeanNameGenerator)sbr.getSingleton("org.springframework.context.annotation.internalConfigurationBeanNameGenerator");
                    this.componentScanBeanNameGenerator = generator;
                    this.importBeanNameGenerator = generator;
                }
            }

            ConfigurationClassParser parser = new ConfigurationClassParser(this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry);
            Set<BeanDefinitionHolder> candidates = new LinkedHashSet(configCandidates); // 一般啟動時,這里就包含了你的啟動類,如DemoApplication
            HashSet alreadyParsed = new HashSet(configCandidates.size());

            do {         
         // 這里進行轉換,對標記了Configuration的類進行搜集(一般是自動配置的類以及它的依賴類),        
         // 像DemoApplication比較特殊,引入了@ComponentScan,所以會將父包下的Configuration類型的類也會進行搜集,所以如果顯示設置配置,可覆蓋自動設置(條件化加載)         
                // 這里的轉換比較復雜,使用了很多的遞歸以及條件依賴(加載A時,先要加載B),暫不做詳細研究,可重新作為另一方面來探討,有興趣的同學可以一起交流,具體流程可參考下圖
                parser.parse(candidates); 
                parser.validate();
                Set<ConfigurationClass> configClasses = new LinkedHashSet(parser.getConfigurationClasses());
                configClasses.removeAll(alreadyParsed);
                if(this.reader == null) {
                    this.reader = new ConfigurationClassBeanDefinitionReader(registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry());
                }
         // 這里主要是對每個標記為Configuration的類加載該類下的Bean配置(@Bean方法,ImportedResources(BeanDefinitionReader)引入的,Registrars注冊器引入的)         
         // 如果自身是需要被引用的,首先將自身注冊為bean,再去加載該類的bean配置  
                this.reader.loadBeanDefinitions(configClasses);
                alreadyParsed.addAll(configClasses);
                candidates.clear();
                if(registry.getBeanDefinitionCount() > candidateNames.length) {
                    String[] newCandidateNames = registry.getBeanDefinitionNames();
                    Set<String> oldCandidateNames = new HashSet(Arrays.asList(candidateNames));
                    Set<String> alreadyParsedClasses = new HashSet();
                    Iterator var12 = alreadyParsed.iterator();

                    while(var12.hasNext()) {
                        ConfigurationClass configurationClass = (ConfigurationClass)var12.next();
                        alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
                    }

                    String[] var23 = newCandidateNames;
                    int var24 = newCandidateNames.length;
            // 已注冊的bean,判斷是沒有進行轉換的,則進行轉換(candidates判空循環)
                    for(int var14 = 0; var14 < var24; ++var14) {
                        String candidateName = var23[var14];
                        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());

            if(sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
                sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
            }

            if(this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
                ((CachingMetadataReaderFactory)this.metadataReaderFactory).clearCache();
            }

        }
    }

    自動配置的類加載過程:

                         圖2

  在processImports方法中,會加載自動配置所對應的類(spring.factories下的配置)。

  以上純屬個人理解,如有錯誤,請見諒,如可以請聯系我,讓我把錯誤修正,感謝。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM