美文网首页
玩转springboot之springboot自动配置原理

玩转springboot之springboot自动配置原理

作者: 墨线宝 | 来源:发表于2024-07-15 10:03 被阅读0次

    自动配置原理

    自动配置其实是由于启动类上配置了@EnableAutoConfiguration注解来进行开启的,那有些人就纳闷了,我启动类上也没有配置@EnableAutoConfiguration注解呀?那是因为@SpringBootApplication是组合注解,其内部包含有@EnableAutoConfiguration注解

    基于springboot2.x版本,与1.x版本不同,不过思路相似

    主启动类

    @SpringBootApplication //标注主程序类
    public class DemoApplication {
       
       public static void main(String[] args) {
           SpringApplication.run(DemoApplication.class, args);
       }
    }
    

    SpringBootApplication注解源码

    @SpringBootApplication是一个组合注解,@SpringBootApplication = @Configuration + @EnableAutoConfiguration + @ComponentScan

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @SpringBootConfiguration
    @EnableAutoConfiguration
    @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
            @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
    public @interface SpringBootApplication 
    
    

    @Configuration注解

    @Configuration注解就相当于一个xml配置文件,可以在该类中配置bean,就像在xml中配置bean一样

    @ComponentScan注解

    对于xml配置文件中的<context:component-scan/>,用来进行组件扫描的,将bean加载到容器中

    @EnableAutoConfiguration注解

    @EnableAutoConfiguration注解就是用来进行自动配置的,也是springboot的核心,@EnableAutoConfiguration注解是如何做到的自动配置呢?主要利用了一个AutoConfigurationImportSelector选择器给Spring容器中来导入一些组件

    @EnableAutoConfiguration源码
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @AutoConfigurationPackage // 该注解内为@Import(AutoConfigurationPackages.Registrar.class)
    @Import(AutoConfigurationImportSelector.class)
    public @interface EnableAutoConfiguration {
    
        String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
    
        
        Class<?>[] exclude() default {};
    
        
        String[] excludeName() default {};
    
    }
    

    可以看到Import了一个AutoConfigurationImportSelector组件,这个就是自动配置的真相了

    AutoConfigurationImportSelector导入组件

    主要是来读取META-INF/spring.factories来进行加载各个自动配置类

    public String[] selectImports(AnnotationMetadata annotationMetadata) {
      // 可以使用spring.boot.enableautoconfiguration配置来关闭或开启自动配置
            if (!isEnabled(annotationMetadata)) {
                return NO_IMPORTS;
            }
      // META-INF/spring-autoconfigure-metadata.properties 这个文件存储的是”待自动装配候选类“的过滤条件,框架会根据里面的规则逐一对候选类进行计算看是否需要被自动装配进容器
         // 从配置文件 spring-autoconfigure-metadata.properties 获得自动装配类过滤相关的配置
            AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
                    .loadMetadata(this.beanClassLoader);
      
        // 根据过滤规则筛选出来配置
      // NO2
            AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
                    annotationMetadata);
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }
    
    // NO2
    protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
                AnnotationMetadata annotationMetadata) {
            if (!isEnabled(annotationMetadata)) {
                return EMPTY_ENTRY;
            }
            AnnotationAttributes attributes = getAttributes(annotationMetadata);
      // META-INF/spring.factories
         // 借助SpringFactoriesLoader从配置文件 spring.factories 扫描自动配置,key为EnableAutoConfiguration类的
            List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
      // 去除重复的
            configurations = removeDuplicates(configurations);
        // @EnableAutoConfiguration注解中配置的排除项 exclude和excludeName
            Set<String> exclusions = getExclusions(annotationMetadata, attributes);
            checkExcludedClasses(configurations, exclusions);
      // 去除掉排除项
            configurations.removeAll(exclusions);
        // 根据  spring-autoconfigure-metadata.properties 中的配置进行过滤
      // NO3
            configurations = filter(configurations, autoConfigurationMetadata);
            fireAutoConfigurationImportEvents(configurations, exclusions);
            return new AutoConfigurationEntry(configurations, exclusions);
        }
    
    // NO3
    private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {
            long startTime = System.nanoTime();
            String[] candidates = StringUtils.toStringArray(configurations);
            boolean[] skip = new boolean[candidates.length];
            boolean skipped = false;
      // 拿到OnBeanCondition,OnClassCondition和OnWebApplicationCondition, 然后遍历这三个条件类去过滤从spring.factories加载的大量配置类
            for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
          // 调用各种aware方法,将beanClassLoader,beanFactory等注入到filter对象中
                invokeAwareMethods(filter);
          // 判断各种filter来判断每个candidate @ConditionalOnClass,@ConditionalOnBean和@ConditionalOnWebApplication里面的注解值是否匹配
                boolean[] match = filter.match(candidates, autoConfigurationMetadata);
                for (int i = 0; i < match.length; i++) {
                    if (!match[i]) {
              // 不匹配的将记录在skip数组,标志skip[i]为true,也与candidates数组一一对应
                        skip[i] = true;
                        candidates[i] = null;
                        skipped = true;
                    }
                }
            }
      // 若所有自动配置类经过OnBeanCondition,OnClassCondition和OnWebApplicationCondition过滤后,全部都匹配的话,则全部原样返回
            if (!skipped) {
                return configurations;
            }
            List<String> result = new ArrayList<>(candidates.length);
            for (int i = 0; i < candidates.length; i++) {
          // 若skip[i]为false,则说明是符合条件的自动配置类,此时添加到result集合中
                if (!skip[i]) {
                    result.add(candidates[i]);
                }
            }
            
            return new ArrayList<>(result);
        }
    
    SpringFactoriesLoader详解

    该类的主要功能就是从META-INF/spring.factories加载配置,该配置文件是key=value格式,key和value都是java类型的全类名,其根据某个类型的名称作为key去进行查找,如使用org.springframework.boot.autoconfigure.EnableAutoConfiguration去查找到对应的配置项,然后通过反射实例化对应的配置类

    调用点

    这个注解是什么时候解析的呢?

    在进行上下文初始化的时候

    // org.springframework.context.support.AbstractApplicationContext#refresh中调用
    invokeBeanFactoryPostProcessors(beanFactory);
    
    // 该方法会执行PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors())
    
    // 执行invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry)
    // currentRegistryProcessors为org.springframework.context.annotation.ConfigurationClassPostProcessor
    // 执行postProcessor.postProcessBeanDefinitionRegistry(registry);
    // 执行processConfigBeanDefinitions(registry)
    
    public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
            List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
            String[] candidateNames = registry.getBeanDefinitionNames();
    
            for (String beanName : candidateNames) {
                BeanDefinition beanDef = registry.getBeanDefinition(beanName);
                if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null)            {
                }
                else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
                    configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
                }
            }
    
            // Return immediately if no @Configuration classes were found
            if (configCandidates.isEmpty()) {
                return;
            }
    
            // Sort by previously determined @Order value, if applicable
            configCandidates.sort((bd1, bd2) -> {
                int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
                int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
                return Integer.compare(i1, i2);
            });
    
            // Detect any custom bean name generation strategy supplied through the enclosing application context
            SingletonBeanRegistry sbr = null;
            if (registry instanceof SingletonBeanRegistry) {
                sbr = (SingletonBeanRegistry) registry;
                if (!this.localBeanNameGeneratorSet) {
                    BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(
                            AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
                    if (generator != null) {
                        this.componentScanBeanNameGenerator = generator;
                        this.importBeanNameGenerator = generator;
                    }
                }
            }
    
            if (this.environment == null) {
                this.environment = new StandardEnvironment();
            }
    
            // Parse each @Configuration class
      // 解析@Configuration注解的类
            ConfigurationClassParser parser = new ConfigurationClassParser(
                    this.metadataReaderFactory, this.problemReporter, this.environment,
                    this.resourceLoader, this.componentScanBeanNameGenerator, registry);
            // 这里就是主类,因为主类也配置了@Configuration注解
            Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
            Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
            do {
                parser.parse(candidates);
                parser.validate();
    
                Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
                configClasses.removeAll(alreadyParsed);
    
                // Read the model and create bean definitions based on its content
                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<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
                    Set<String> 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());
    
            // Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
            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();
            }
        }
    

    在parse方法中会调用

    this.deferredImportSelectorHandler.process()
    

    process方法

    public void process() {
       List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
       this.deferredImportSelectors = null;
       try {
          if (deferredImports != null) {
             DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
             deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
             deferredImports.forEach(handler::register);
             handler.processGroupImports();
          }
       }
       finally {
          this.deferredImportSelectors = new ArrayList<>();
       }
    }
    
    public void processGroupImports() {
      // 这里的grouping中包含的group是org.springframework.boot.autoconfigure.AutoConfigurationImportSelector$AutoConfigurationGroup
                for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
            // 调用group的process方法
                    grouping.getImports().forEach(entry -> {
                        ConfigurationClass configurationClass = this.configurationClasses.get(
                                entry.getMetadata());
                        try {
                            processImports(configurationClass, asSourceClass(configurationClass),
                                    asSourceClasses(entry.getImportClassName()), false);
                        }
                        catch (BeanDefinitionStoreException ex) {
                            throw ex;
                        }
                        catch (Throwable ex) {
                            throw new BeanDefinitionStoreException(
                                    "Failed to process import candidates for configuration class [" +
                                            configurationClass.getMetadata().getClassName() + "]", ex);
                        }
                    });
                }
            }
    

    也就是说其实是通过AutoConfigurationImportSelector的静态内部类的process方法来调用getAutoConfigurationEntry获取自动配置类的,而不是通过AutoConfigurationImportSelector#selectImports来调用的

    // org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#process
    public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
       Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
             () -> String.format("Only %s implementations are supported, got %s",
                   AutoConfigurationImportSelector.class.getSimpleName(),
                   deferredImportSelector.getClass().getName()));
       AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
             .getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
       this.autoConfigurationEntries.add(autoConfigurationEntry);
       for (String importClassName : autoConfigurationEntry.getConfigurations()) {
          this.entries.putIfAbsent(importClassName, annotationMetadata);
       }
    }
    

    https://zhhll.icu/2021/框架/springboot/源码/1.自动配置原理/

    相关文章

      网友评论

          本文标题:玩转springboot之springboot自动配置原理

          本文链接:https://www.haomeiwen.com/subject/idhhhjtx.html