玩转springboot之springboot自动配置原理

自动配置原理

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

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

主启动类

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

SpringBootApplication注解源码

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

java 复制代码
@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源码
java 复制代码
@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来进行加载各个自动配置类

java 复制代码
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去查找到对应的配置项,然后通过反射实例化对应的配置类

调用点

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

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

java 复制代码
// 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)
java 复制代码
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方法中会调用

java 复制代码
this.deferredImportSelectorHandler.process()

process方法

java 复制代码
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来调用的

java 复制代码
// 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);
   }
}

zhhll.icu/2021/框架/spr...

相关推荐
MacroZheng1 小时前
还在用WebSocket实现即时通讯?试试MQTT吧,真香!
java·spring boot·后端
midsummer_woo1 小时前
基于springboot的IT技术交流和分享平台的设计与实现(源码+论文)
java·spring boot·后端
别惹CC3 小时前
Spring AI 进阶之路01:三步将 AI 整合进 Spring Boot
人工智能·spring boot·spring
柯南二号4 小时前
【Java后端】Spring Boot 集成 MyBatis-Plus 全攻略
java·spring boot·mybatis
javachen__5 小时前
SpringBoot整合P6Spy实现全链路SQL监控
spring boot·后端·sql
IT毕设实战小研11 小时前
基于Spring Boot 4s店车辆管理系统 租车管理系统 停车位管理系统 智慧车辆管理系统
java·开发语言·spring boot·后端·spring·毕业设计·课程设计
一只爱撸猫的程序猿12 小时前
使用Spring AI配合MCP(Model Context Protocol)构建一个"智能代码审查助手"
spring boot·aigc·ai编程
甄超锋13 小时前
Java ArrayList的介绍及用法
java·windows·spring boot·python·spring·spring cloud·tomcat
武昌库里写JAVA15 小时前
JAVA面试汇总(四)JVM(一)
java·vue.js·spring boot·sql·学习
Pitayafruit16 小时前
Spring AI 进阶之路03:集成RAG构建高效知识库
spring boot·后端·llm