• 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下的配置)。

      以上纯属个人理解,如有错误,请见谅,如可以请联系我,让我把错误修正,感谢。

  • 相关阅读:
    [Todo]很不错的Java面试题类型整理,要看
    [Todo] Java并发编程学习
    自建一个Java Spring MVC项目
    [Todo] 乐观悲观锁,自旋互斥锁等等
    [Todo] Redis里面队列的两种模式,以及抢红包在Redis中的实现
    hdu 4704 同余定理+普通快速幂
    [置顶] ubuntu 和 win7 远程登陆 + vnc登陆
    mysql之触发器
    Jsoup API解析HTML中input标签
    IOS UITableView单条刷新,数据不刷新解决方案
  • 原文地址:https://www.cnblogs.com/zgz21/p/8033515.html
Copyright © 2020-2023  润新知