IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Alian解读SpringBoot 2.6.0 源码(十):启动流程之自动装配原理 -> 正文阅读

[Java知识库]Alian解读SpringBoot 2.6.0 源码(十):启动流程之自动装配原理

一、背景

??你真的了解Spring Boot 自动装配的原理么?很多小伙伴就直接开始从主类上的注解@SpringBootApplication 开始点击,然后一直点击到类org.springframework.boot.autoconfigure.AutoConfigurationImportSelector,然后会执行哪里哪里,估计也是人云亦云,有没有想过为啥会要走@SpringBootApplication ?什么时候走?好了,废话不多少,我们来探索真正的自动装配原理。

1.1、主类的加载

??其实我在之前的文章Alian解读SpringBoot 2.6.0 源码(七):启动流程分析之准备应用上下文 就讲到了很重要的一部分知识,那就是主类资源的加载和注册。希望大家看完我下图标注的,再看下去会比较容易理解。
在这里插入图片描述

1.2、后置处理器的获取

??同样的我在之前的文章Alian解读SpringBoot 2.6.0 源码(八):启动流程分析之刷新应用上下文(中) 就讲到了后置处理器ConfigurationClassPostProcessor 的获取,具体如下图:
在这里插入图片描述
??认真理解我所说的这两个点后,开始我们的解读,因为这些我在之前的文章就已经解读很细了,这里只是引用而已,具体的还是需要大家自己认真调试到这里,具体的代码如下:

final class PostProcessorRegistrationDelegate {

	private PostProcessorRegistrationDelegate() {
	}

	// 此方法最重要的是要确保注册和调用按照指定的顺序,比如PriorityOrdered和Ordered 
	public static void invokeBeanFactoryPostProcessors(
			ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
		// 定义Set集合processedBeans 
		Set<String> processedBeans = new HashSet<>();
		// beanFactory 就是DefaultListableBeanFactory,它实现了接口BeanDefinitionRegistry
		// 故此处是BeanDefinitionRegistry的实例
		if (beanFactory instanceof BeanDefinitionRegistry) {
			// 转为BeanDefinitionRegistry 
			BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
			// 定义BeanFactoryPostProcessor列表
			List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
			// 定义BeanDefinitionRegistryPostProcessor列表,属于BeanDefinitionRegistryPostProcessor的实例的
			List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
			// 从上一步我们知道beanFactoryPostProcessors有两个:
			// CachingMetadataReaderFactoryPostProcessor 和 ConfigurationWarningsPostProcessor
			// 开始遍历后置处理器beanFactoryPostProcessors
			for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
				// 都实现了BeanDefinitionRegistryPostProcessor接口
				if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
					// 转为BeanDefinitionRegistryPostProcessor 
					BeanDefinitionRegistryPostProcessor registryProcessor = (BeanDefinitionRegistryPostProcessor) postProcessor;
					// 调用后置处理器的postProcessBeanDefinitionRegistry
					registryProcessor.postProcessBeanDefinitionRegistry(registry);
					// 加入到BeanDefinitionRegistryPostProcessor列表
					registryProcessors.add(registryProcessor);
				} else {
					// 加入到BeanFactoryPostProcessor列表
					regularPostProcessors.add(postProcessor);
				}
			}
			// 定义当前调用的BeanDefinitionRegistryPostProcessor列表
			// 同样不要在这里初始化FactoryBeans:需要确保所有常规Bean未初始化以使beanFactory后处理器应用于它们
			List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();

			// 首先,调用实现PriorityOrdered接口的BeanDefinitionRegistryPostProcessor
			String[] postProcessorNames =
					beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
			for (String ppName : postProcessorNames) {
				// 是否实现了PriorityOrdered接口
				if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
					// 获取对应的后处理器,添加到当前调用的BeanDefinitionRegistryPostProcessor列表
					currentRegistryProcessors.add(beanFactory.getBean(ppName,BeanDefinitionRegistryPostProcessor.class));
					// 添加到Set集合processedBeans
					processedBeans.add(ppName);
				}
			}
			// 排序,此处得到的就是ConfigurationClassPostProcessor
			sortPostProcessors(currentRegistryProcessors, beanFactory);
			// 把当前实现了PriorityOrdered接口BeanDefinitionRegistryPostProcessor列表都加入到BeanDefinitionRegistryPostProcessor列表
			registryProcessors.addAll(currentRegistryProcessors);
			// 循环调用postProcessBeanDefinitionRegistry()方法(实现了PriorityOrdered接口)
			invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
			// 后续的代码省略,只需要到上面这一行即可
			// 后续的代码省略
			// 后续的代码省略
		} else {
			// 后续的代码省略
		}
	}

	//上面就是调用本类的这个方法
	private static void invokeBeanDefinitionRegistryPostProcessors(
			Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry, ApplicationStartup applicationStartup) {

		for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
			StartupStep postProcessBeanDefRegistry = applicationStartup.start("spring.context.beandef-registry.post-process")
					.tag("postProcessor", postProcessor::toString);
			//核心调用是下面这句
			postProcessor.postProcessBeanDefinitionRegistry(registry);
			postProcessBeanDefRegistry.end();
		}
	}
}

??因为postProcessors得到就只有一个对象,那就是ConfigurationClassPostProcessor ,接下里我们就去看看是如何一步步调用的。

二、配置类后处理器

2.1、获取配置类

??具体的类路径:org.springframework.context.annotation.ConfigurationClassPostProcessor

public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
		PriorityOrdered, ResourceLoaderAware, ApplicationStartupAware, BeanClassLoaderAware, EnvironmentAware{
	
	//从注册表中的配置类派生进一步的bean定义。
	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
		int registryId = System.identityHashCode(registry);
		if (this.registriesPostProcessed.contains(registryId)) {
			throw new IllegalStateException("postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
		}
		if (this.factoriesPostProcessed.contains(registryId)) {
			throw new IllegalStateException("postProcessBeanFactory already called on this post-processor against " + registry);
		}
		this.registriesPostProcessed.add(registryId);
		//处理配置bean的定义
		processConfigBeanDefinitions(registry);
	}

	//基于配置类的注册表构建并验证配置模型。
	public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
		//定义列表configCandidates用于存放配置类
		List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
		//获取注册表中定义的所有bean的名称,如果未定义,则为空数组
		//本版本为springboot2.6.0,一共有7个(当然如果你自己也定义了配置类或者增加了其他的依赖会更多)
		String[] candidateNames = registry.getBeanDefinitionNames();
		//遍历bean的名称列表
		for (String beanName : candidateNames) {
			//通过名称后去bean的定义
			BeanDefinition beanDef = registry.getBeanDefinition(beanName);
			if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
				if (logger.isDebugEnabled()) {
					logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
				}
			}
			//判断是否符合配置类的资格,比如包含@Configuration注解(里面会扫描类的注解及父注解)
			else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
				//是配置类则加入配置类候选列表
				configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
			}
		}
		// 如果未找到@Configuration类,则立即返回
		if (configCandidates.isEmpty()) {
			return;
		}

		// 按先前确定的@Order值排序
		configCandidates.sort((bd1, bd2) -> {
			int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
			int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
			return Integer.compare(i1, i2);
		});
		//检测通过封闭应用程序上下文提供的任何自定义bean名称生成策略
		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;
				}
			}
		}
		//本文environment是ApplicationServletEnvironment
		if (this.environment == null) {
			this.environment = new StandardEnvironment();
		}
		//创建一个新的ConfigurationClassParser实例,该实例将用于填充配置类集。
		ConfigurationClassParser parser = new ConfigurationClassParser(
				this.metadataReaderFactory, this.problemReporter, this.environment,
				this.resourceLoader, this.componentScanBeanNameGenerator, registry);
		//list转为set
		Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
		//初始化已经解析的配置集合
		Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
		do {
			StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");
			// 解析每个@Configuration类,这个是核心
			parser.parse(candidates);
			// 校验配置类
			parser.validate();
			//此后的代码省略
			//此后的代码省略
			//此后的代码省略
		}
		while (!candidates.isEmpty());
		//此后的代码省略
		//此后的代码省略
		//此后的代码省略
	}
}

??而得到的candidateNames就是如下列表:

  • org.springframework.context.annotation.internalConfigurationAnnotationProcessor
  • org.springframework.context.annotation.internalAutowiredAnnotationProcessor
  • org.springframework.context.annotation.internalCommonAnnotationProcessor
  • org.springframework.context.event.internalEventListenerProcessor
  • org.springframework.context.event.internalEventListenerFactory
  • springbootApplication
  • org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory

??实际上倒数第二个就是我们的主类

package com.alian.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringbootApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootApplication.class, args);
    }

}
  • 获取注册表中定义的所有bean的名称列表
  • 遍历候选列表,如果是配置类则加入待解析的列表,此过程里面会扫描类的注解及父注解
  • 把获取到的列表按照order排序,实际上就只有我们的主类springbootApplication
  • 创建一个新的ConfigurationClassParser实例,然后解析每个@Configuration

2.2、

??具体的类路径:org.springframework.context.annotation.ConfigurationClassParser

class ConfigurationClassParser {

	public void parse(Set<BeanDefinitionHolder> configCandidates) {
		for (BeanDefinitionHolder holder : configCandidates) {
			BeanDefinition bd = holder.getBeanDefinition();
			try {
				if (bd instanceof AnnotatedBeanDefinition) {
					//我们的注解是带注解@SpringBootApplication的bean
					parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
				} else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
					parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
				} else {
					parse(bd.getBeanClassName(), holder.getBeanName());
				}
			} catch (BeanDefinitionStoreException ex) {
				throw ex;
			} catch (Throwable ex) {
				throw new BeanDefinitionStoreException( "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
			}
		}
		//核心处理
		this.deferredImportSelectorHandler.process();
	}
}

这里有两个重要步骤

  • 解析我们的主类
  • Import Select 处理程序的执行

2.3、解析主类

2.3.1、整体解析过程

??同样的具体的类路径:org.springframework.context.annotation.ConfigurationClassParser,我们先看看主类的解析。

class ConfigurationClassParser {

	protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
		processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);
	}

	protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
		//根据@Conditional 注解确定是否应跳过项
		if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
			return;
		}
		//是否已解析过
		ConfigurationClass existingClass = this.configurationClasses.get(configClass);
		//明显我们的主类还未解析existingClass 为null
		if (existingClass != null) {
			if (configClass.isImported()) {
				if (existingClass.isImported()) {
					existingClass.mergeImportedBy(configClass);
				}
				// Otherwise ignore new imported config class; existing non-imported class overrides it.
				return;
			} else {
				// Explicit bean definition found, probably replacing an import.
				// Let's remove the old one and go with the new one.
				this.configurationClasses.remove(configClass);
				this.knownSuperclasses.values().removeIf(configClass::equals);
			}
		}

		// 递归处理配置类及其超类层次结构
		SourceClass sourceClass = asSourceClass(configClass, filter);
		do {
			//核心解析过程
			sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
		}
		while (sourceClass != null);
		//放到已解析的map中
		this.configurationClasses.put(configClass, configClass);
	}
}
  • 根据@Conditional 注解确定是否应跳过项
  • 从已解析的map中获取对象,并判断该配置类是否已解析过
  • 递归处理配置类及其超类层次结构
  • 把解析的配置类放到已解析的map中

2.3.2、核心解析过程

??我继续看下doProcessConfigurationClass方法,同样的具体的类路径还是:org.springframework.context.annotation.ConfigurationClassParser

	//通过读取源类(我们的主类)中的注释、成员和方法,应用处理并构建完整的ConfigurationClass
	protected final SourceClass doProcessConfigurationClass(
			ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
			throws IOException {
		//判断是否有@Component注解
		if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
			// 如果有递归处理任何成员(嵌套)类
			processMemberClasses(configClass, sourceClass, filter);
		}

		// 处理带@PropertySource注解的类(主类中没有)
		for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), PropertySources.class,
				org.springframework.context.annotation.PropertySource.class)) {
			if (this.environment instanceof ConfigurableEnvironment) {
				processPropertySource(propertySource);
			}
			else {
				logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
						"]. Reason: Environment must implement ConfigurableEnvironment");
			}
		}

		// 处理带@ComponentScan注解的类
		Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
		if (!componentScans.isEmpty() &&
				!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
			for (AnnotationAttributes componentScan : componentScans) {
				// The config class is annotated with @ComponentScan -> perform the scan immediately
				Set<BeanDefinitionHolder> scannedBeanDefinitions =
						this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
				// Check the set of scanned definitions for any further config classes and parse recursively if needed
				for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
					BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
					if (bdCand == null) {
						bdCand = holder.getBeanDefinition();
					}
					if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
						parse(bdCand.getBeanClassName(), holder.getBeanName());
					}
				}
			}
		}

		// 处理带@Import 注解的类
		processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

		// 处理带@ImportResource 注解的类
		AnnotationAttributes importResource =
				AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
		if (importResource != null) {
			String[] resources = importResource.getStringArray("locations");
			Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
			for (String resource : resources) {
				String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
				configClass.addImportedResource(resolvedResource, readerClass);
			}
		}

		// 处理单个@Bean方法
		Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
		for (MethodMetadata methodMetadata : beanMethods) {
			configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
		}

		// 处理接口上的默认方法
		processInterfaces(configClass, sourceClass);

		// 如果有父类则处理父类
		if (sourceClass.getMetadata().hasSuperClass()) {
			String superclass = sourceClass.getMetadata().getSuperClassName();
			if (superclass != null && !superclass.startsWith("java") &&
					!this.knownSuperclasses.containsKey(superclass)) {
				this.knownSuperclasses.put(superclass, configClass);
				// Superclass found, return its annotation metadata and recurse
				return sourceClass.getSuperClass();
			}
		}

		// 没有父类->处理完成
		return null;
	}
  • 如果有@Component 注解,递归处理任何成员(嵌套)类
  • 处理带@PropertySource 注解的类
  • 处理带@ComponentScan注解的类,这里可能会递归
  • 处理带@Import 注解的类
  • 处理带@ImportResource 注解的类
  • 处理单个@Bean 方法
  • 处理接口上的默认方法
  • 如果有父类则处理父类,没有则处理完成

2.3.3、延迟导入选择器处理程序

??我继续看下DeferredImportSelectorHandlerprocess()方法,它是:org.springframework.context.annotation.ConfigurationClassParser的内部类

class ConfigurationClassParser {

	private List<DeferredImportSelectorHolder> deferredImportSelectors = new ArrayList<>();

	private class DeferredImportSelectorHandler {

		public void process() {
			List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
			this.deferredImportSelectors = null;
			try {
				if (deferredImports != null) {
					DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
					deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
					//遍历列表并调用register方法获取ImportGroup,此处是得到AutoConfigurationGroup.class
					deferredImports.forEach(handler::register);
					//处理GroupImports
					handler.processGroupImports();
				}
			}finally {
				this.deferredImportSelectors = new ArrayList<>();
			}
		}
	}
}
  • 获取延迟导入选择器,其实在解析阶段处理带@Import 注解的类时就初始化了DeferredImportSelectorHolder到列表deferredImportSelectors 中了,我们的EnableAutoConfiguration 也是有@Import 注解的,自然
    AutoConfigurationImportSelector就是被扫描注入了(这个很关键哦)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

	/**
	 * Environment property that can be used to override when auto-configuration is
	 * enabled.
	 */
	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

	/**
	 * Exclude specific auto-configuration classes such that they will never be applied.
	 * @return the classes to exclude
	 */
	Class<?>[] exclude() default {};

	/**
	 * Exclude specific auto-configuration class names such that they will never be
	 * applied.
	 * @return the class names to exclude
	 * @since 1.3.0
	 */
	String[] excludeName() default {};

}
  • 遍历延迟导入选择器分组处理程序,调用register方法,此步骤会获取导入组,实际就是实现了DeferredImportSelector接口类,而上面我们注入的导入的类AutoConfigurationImportSelector就是实现了DeferredImportSelector接口,从而返回了导入组AutoConfigurationGroup.class,而它是AutoConfigurationImportSelector的一个内部类
  • 延迟导入选择器分组处理程序的执行

2.3.4、延迟导入选择器分组处理程序的注册和执行

private class DeferredImportSelectorGroupingHandler {

	private final Map<Object, DeferredImportSelectorGrouping> groupings = new LinkedHashMap<>();

	private final Map<AnnotationMetadata, ConfigurationClass> configurationClasses = new HashMap<>();

	public void register(DeferredImportSelectorHolder deferredImport) {
		//deferredImport.getImportSelector()获得AutoConfigurationImportSelector
		//然后调用getImportGroup()得到AutoConfigurationGroup.class
		Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
		//创建DeferredImportSelectorGrouping的实例
		//分组选择器和延迟分组选择器组的映射
		DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent(
				(group != null ? group : deferredImport),
				key -> new DeferredImportSelectorGrouping(createGroup(group)));
		grouping.add(deferredImport);
		//延迟导入的配置类的元数据和延迟导入的配置类的映射
		this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
				deferredImport.getConfigurationClass());
	}

	public void processGroupImports() {
		for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
			Predicate<String> exclusionFilter = grouping.getCandidateFilter();
			//这里的关键就是grouping.getImports()方法
			grouping.getImports().forEach(entry -> {
				//中间代码省略
				//中间代码省略
				//中间代码省略
			});
		}
	}
}

注册时:

  • deferredImport.getImportSelector()获得AutoConfigurationImportSelector
  • 然后调用getImportGroup()得到AutoConfigurationGroup.class
  • 创建DeferredImportSelectorGrouping 的实例,并组装映射
  • 配置类映射中存放延迟导入的配置类的元数据和延迟导入的配置类的映射

执行时:

  • 从上面的注册知道遍历的groupings里就是AutoConfigurationImportSelector
  • 然后执行DeferredImportSelectorGroupinggetImports()
class ConfigurationClassParser {
	private static class DeferredImportSelectorGrouping {
		public Iterable<Group.Entry> getImports() {
			//遍历延迟导入选择器处理器,它有两个关键属性
			//一个是configurationClass:我们的主类com.alian.springboot.SpringbootApplication
			//一个是importSelector:org.springframework.boot.autoconfigure.AutoConfigurationImportSelector
			for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
				//这个地方才是自动装配的入口
				this.group.process(deferredImport.getConfigurationClass().getMetadata(),
						deferredImport.getImportSelector());
			}
			//返回每个导入及其关联的配置类
			return this.group.selectImports();
		}
	}
}

??关键方法 process 第一个参数就是元数据 ,这里的值是:com.alian.springboot.SpringbootApplication ;第二个参数是延迟导入选择器 ,这里的值是:AutoConfigurationImportSelector ,然后调用AutoConfigurationImportSelector process方法,这样才是真的进入到这个类的执行。

2.4、自动配置获取

2.4.1、自动配置组执行

AutoConfigurationImportSelector.java

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
		
	private final Map<String, AnnotationMetadata> entries = new LinkedHashMap<>();

	private final List<AutoConfigurationEntry> autoConfigurationEntries = new ArrayList<>();
		
	private static class AutoConfigurationGroup
			implements DeferredImportSelector.Group, BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware {
			
		@Override
		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(annotationMetadata);
			//加入到自动配置列表,每个选择器对应响应的实体
			this.autoConfigurationEntries.add(autoConfigurationEntry);
			//遍历列表
			for (String importClassName : autoConfigurationEntry.getConfigurations()) {
				//把导入类名和元数据映射起来
				//这里的类名就是自动配置的类名
				//元数据就是我们的主类元数据
				this.entries.putIfAbsent(importClassName, annotationMetadata);
			}
		}
	}

	//返回自动配置ImportSelector。基于导入@Configuration类的AnnotationMetadata的自动配置实体。
	protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		//获取元数据上的注解属性
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
		//根据元数据和注解属性获取候选配置类(核心方法)
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
		//移除重复配置类
		configurations = removeDuplicates(configurations);
		//获得注解中被exclude和excludeName排除的类的集合
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		//检查被排除类(不符合条件则抛出异常,比如是否可实例化、是否被自动注册配置所使用)
		checkExcludedClasses(configurations, exclusions);
		//在候选配置类中去除掉被排除的类
		configurations.removeAll(exclusions);
		//过滤配置类
		configurations = getConfigurationClassFilter().filter(configurations);
		//将配置类和排除类通过事件传入到监听器中
		fireAutoConfigurationImportEvents(configurations, exclusions);
		//返回符合条件的自动配置类的全限定名数组
		return new AutoConfigurationEntry(configurations, exclusions);
	}

	protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
		//Spring工厂加载机制,这个我讲过了
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader());
		Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
		return configurations;
	}

	protected Class<?> getSpringFactoriesLoaderFactoryClass() {
		return EnableAutoConfiguration.class;
	}
}

?? process方法主要还是调用核心的方法 getAutoConfigurationEntry,我们说下它大概的流程:

  • 获取注解元数据上的注解属性
  • 根据元数据和注解属性获取候选配置类(核心方法)
  • 移除重复配置类
  • 获得注解元数据中被exclude和excludeName排除的类的集合
  • 检查被排除的类,在候选配置类中去除掉被排除的类,并过滤配置类
  • 将配置类和排除类通过事件传入到监听器中
  • 返回符合条件的自动配置类的全限定名数组

2.4.2、loadFactoryNames

??此方法所在类的具体路径:org.springframework.core.io.support.SpringFactoriesLoader

	public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
		// 此处的类加载器是appClassLoader
		ClassLoader classLoaderToUse = classLoader;
		if (classLoaderToUse == null) {
			//如果为空则再次获取
			classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
		}
		// 类的全限定名称(即解析properties文件中需要的key值)
		String factoryTypeName = factoryType.getName();
		// 根据类加载器,加载classpath下/META-INF/spring.factories下所有的类名称列表
		// 从结果Map<String, List<String>>中,根据指定类型获取所有实现类名称的集合List<String>
		// 核心
		return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
	}
  • loadSpringFactories(ClassLoader classLoader)返回的是所有META-INF/spring.factories文件解析完的结果
  • 根据指定类名获取结果,没有则返回空列表

2.4.3、loadSpringFactories

??此方法所在类的具体路径:org.springframework.core.io.support.SpringFactoriesLoader

	public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

	private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
		// 根据类加载器去缓存中获取加载好的结果集
		//因为SpringApplication实例化时就加载过一次了,这里就是从缓存中获取到值了
		Map<String, List<String>> result = cache.get(classLoader);
		if (result != null) {
			//结果不为空,则返回
			return result;
		}
		//已从缓存获取到结果,后面代码省略
		//已从缓存获取到结果,后面代码省略
		//已从缓存获取到结果,后面代码省略
	}

??因为SpringApplication实例化时就加载过一次了,这里就是从缓存中获取到值了,讲到这里我想就不用我多讲了,结合我之前的文章Alian解读SpringBoot 2.6.0 源码(一):SpringApplication对象创建(Spring工厂加载机制)就完全清楚了,我这篇文章里把超级详细的加载过程都讲解了,这里就不在占用篇幅了。只不过本文中就是加载实现了org.springframework.boot.autoconfigure.EnableAutoConfiguration 的类而已。也就是从META-INF/spring.factories文件中获取EnableAutoConfiguration的实现类,我这个版本是133个,最终符合条件的配置类就是24个:

  • org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration
  • org.springframework.boot.autoconfigure.aop.AopAutoConfiguration
  • org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration
  • org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration
  • org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration
  • org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration
  • org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration
  • org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration
  • org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration
  • org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration
  • org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration
  • org.springframework.boot.autoconfigure.availability.ApplicationAvailabilityAutoConfiguration
  • org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration
  • org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration
  • org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration
  • org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration
  • org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration
  • org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration
  • org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration
  • org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration
  • org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration
  • org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration
  • org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
  • org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration

??这个时候就得到了需要进行自动装配的类了,章节2.3.4后面有个调用(this.group.selectImports())返回每个导入及其关联的配置类,它调用的就是AutoConfigurationGroup selectImports()方法,后续遍历结果,一个个执行导入的配置类,完成最后的自动配置操作。

结语

??从这里看到,我们只需要把要加载的类配置到META-INF/Spring.factories 文件里,SpringBoot 会自动去加载它,很方便我们自己或者是第三方去扩展,我们也可以实现自己 starter,我之前写Alian 的 Spring Cloud就写了很多的starter。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-09-15 01:51:23  更:2022-09-15 01:51:47 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 13:16:23-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码