玩转springboot之springboot自动配置原理

07-17 840阅读

自动配置原理

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

玩转springboot之springboot自动配置原理
(图片来源网络,侵删)

基于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配置文件中的,用来进行组件扫描的,将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 configurations = getCandidateConfigurations(annotationMetadata, attributes);
  // 去除重复的
		configurations = removeDuplicates(configurations);
  	// @EnableAutoConfiguration注解中配置的排除项 exclude和excludeName
		Set 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 filter(List 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  
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 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 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);
			// 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 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());
		// 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 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.自动配置原理/

VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]