【框架源码】SpringBoot核心源码解读之自动配置源码分析

SpringBoot流行之前,程序员大多是用SSM框架整合来进行WEB后端开发。这种方式非常麻烦,需要手动引入大量的包,还要配置很多XML文件,光是搭建环境就需要很久。

基于这种的SSM中xml配置的繁琐,后来衍生出SpringBoot。SpringBoot中的自动装载,大大简化了开发者对于配置的相关信息。

问题:什么是SpringBoot自动配置?

  • 当spring容器启动后,一些自动配置类通过@Conditional注解自动装配的IOC容器中
  • 不需要手动去注入,简化了开发,省去了繁琐的配置
  • 自动配置的相关工作就在 @SpringBootApplication这个注解上

我们来看一下@SpringBootApplication这个注解。

@Target({ElementType.TYPE})  //注解的作用范围,用在类,接口,注解等上面
@Retention(RetentionPolicy.RUNTIME) //注解生命周期,runtime,保留在运行时期
@Documented //可以被文档化
@Inherited //可以被子类继承
@SpringBootConfiguration  //里面是@Configuration属于配置类
@EnableAutoConfiguration  //启动自动配置功能
//配置扫描包
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication

@SpringBootApplication 是一个复合注解,由几个核心的注解组成。

  • @SpringBootConfiguration
    • 里面是 @Configuration,代表是一个配置类,说明主程序类也是一个配置类
  • @EnableAutoConfiguration
    • @AutoConfigurationPackage 将指定的一个包下的所有组件导入到容器当中
    • 在@AutoConfigurationPackage 注解中存在一个 @Import({Registrar.class}) 注解,自动配置包就是通过这个完成的。
  • @ComponentScan
    • 指定扫描哪些组件,默认是扫描主程序所在的包以及其子包

它的核心在于@EnableAutoConfiguration这个注解,这里面是加载自动配置的类信息。

@EnableAutoConfiguration注解核心内容

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage //自动配置包
@Import(AutoConfigurationImportSelector.class) //通过import导入满足条件的bean,并加载到spring的ioc容器里面
public @interface EnableAutoConfiguration 

@AutoConfigurationPackage注解核心内容

  • Registrar的作用是扫描包,默认是把主类所在的包和子包里面全部类扫描进容器里面
  • 所以为什么开发springboot项目需要把主类放到最外层目录,不然就对的注解类就找不到
@Import(AutoConfigurationPackages.Registrar.class) //把Registrar导入到spring容器里面

核心逻辑为这段逻辑,一会我们会断点进行调试。

		//获取主程序所在的目录为位置,metadata是元注解信息
		@Override
		public void registerBeanDefinitions(AnnotationMetadata metadata,
				BeanDefinitionRegistry registry) {
			register(registry, new PackageImport(metadata).getPackageName());
		}

下面,我们来看一下@Import(AutoConfigurationImportSelector.class)这个里面都做了哪些操作。其核心就是通过import导入满足条件的bean, 把springboot应用里面符合@Configuration的类,加载到spring的ioc容器里面

	//用于实现动态注册Bean的功能,【批量】导入对象到容器里,根据条件动态地选择需要注册的Bean,并加入Spring容器
  //实现ImportSelector接口,这个接口的selectImports方法会返回一个String数组,数组中的值就是要添加的组件的全类名
  public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
    //加载元数据信息
		AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
				.loadMetadata(this.beanClassLoader);
    //获取需要自动装载的类的信息
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(
				autoConfigurationMetadata, annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}

ok,我们再来看一下getAutoConfigurationEntry()这个方法的逻辑。这个方法主要是根据指定的注解元数据获取自动配置的条目。

protected AutoConfigurationEntry getAutoConfigurationEntry(
			AutoConfigurationMetadata autoConfigurationMetadata,
			AnnotationMetadata annotationMetadata) {
  	//判断是否启用了自动配置
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
  	//获取候选自动配置类列表
		List<String> configurations = getCandidateConfigurations(annotationMetadata,
				attributes);
  	//去除重复的自动配置类
		configurations = removeDuplicates(configurations);
  	//获取需要排除的自动配置类列表
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
  	//检查是否存在需要排除的自动配置类
		checkExcludedClasses(configurations, exclusions);
  	//将需要排除的类从自动配置类列表中移除
		configurations.removeAll(exclusions);
  	//获取配置类过滤器,对候选自动配置类列表进行过滤
		configurations = filter(configurations, autoConfigurationMetadata);
  	//触发自动配置导入事件,并返回一个新的自动配置条目
		fireAutoConfigurationImportEvents(configurations, exclusions);
		return new AutoConfigurationEntry(configurations, exclusions);
	}

我们来看看getCandidateConfigurations()这里面核心逻辑就是去META-INF/spring.factories这个文件中去拉取全部的配置信息。

	protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
			AnnotationAttributes attributes) {
		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;
	}
	public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

好的,接下来我们来调试走下源码流程。

【框架源码】SpringBoot核心源码解读之自动配置源码分析_第1张图片
【框架源码】SpringBoot核心源码解读之自动配置源码分析_第2张图片
在这里插入图片描述
【框架源码】SpringBoot核心源码解读之自动配置源码分析_第3张图片在这里插入图片描述
【框架源码】SpringBoot核心源码解读之自动配置源码分析_第4张图片【框架源码】SpringBoot核心源码解读之自动配置源码分析_第5张图片

【框架源码】SpringBoot核心源码解读之自动配置源码分析_第6张图片
【框架源码】SpringBoot核心源码解读之自动配置源码分析_第7张图片
【框架源码】SpringBoot核心源码解读之自动配置源码分析_第8张图片

【框架源码】SpringBoot核心源码解读之自动配置源码分析_第9张图片
【框架源码】SpringBoot核心源码解读之自动配置源码分析_第10张图片

ok,我们来总结一下,SpringBoot自动装载的全流程。

  • 首先,加载一下元数据信息
  • 获取需要自动装载的类的信息
    • 判断是否启用了自动配置
    • 获取候选自动配置类列表
    • 获取需要排除的自动配置类列表
    • 检查是否存在需要排除的自动配置类
    • 将需要排除的类从自动配置类列表中移除
    • 获取配置类过滤器,对候选自动配置类列表进行过滤
    • 触发自动配置导入事件,并返回一个新的自动配置条目
  • 注册Bean的定义列表

你可能感兴趣的:(#,框架源码,spring,boot,spring,java,后端)