玩转springboot之springboot自动配置原理

自动配置原理

自动配置其实是由于启动类上配置了@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.自动配置原理/

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

拾光师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值