从源码中理解Spring Boot自动装配原理

2022/12/2 14:24:56

本文主要是介绍从源码中理解Spring Boot自动装配原理,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

什么是自动装配

SpringBoot 定义了一套接口规范,这套规范规定:SpringBoot在启动时会扫描外部引用jar包中的META-INF/spring.factories文件,将文件中配置的类型信息加载到Spring容器,并执行类中定义的各种操作。对于外部jar包来说,只需要按照SpringBoot定义的标准,就能将自己的功能装配到SpringBoot中。

二、自动装配的实现原理

自动装配的实现,离不开SpringBootApplication这个核心注解。查看这个注解的源码,我们会发现在SpringBootApplication注解上,存在着几个注解,其中SpringBootConfigurationEnableAutoConfigurationComponentScan这三个注解是需要我们注意的。

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
		@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
	...
}

(1) ComponentScan

扫描被@Component 、@Service注解的bean,该注解默认会扫描启动类所在的包下所有的类 ,可以自定义不扫描某些 bean。如SpringBootApplication注解源码所示,容器中将排除TypeExcludeFilterh和AutoConfigurationExcludeFilter。

(2) EnableAutoConfiguration

启用 SpringBoot 的自动配置机制

(3) Configuration

允许在上下文中注册额外的 bean 或导入其他配置类

2.1 EnableAutoConfiguration详解

@EnableAutoConfiguration是实现自动装配的重要注解,在这个注解上存在以下两个注解:AutoConfigurationPackageImport

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
	...
}
2.1.1 AutoConfigurationPackage

表示对于标注该注解的类的包,应当使用AutoConfigurationPackages注册。实质上,它负责保存标注相关注解的类的所在包路径。使用一个BasePackage类,保存这个路径。然后使用@Import注解将其注入到ioc容器中。这样,可以在容器中拿到该路径。

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
	@Override
	public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
		register(registry, new PackageImport(metadata).getPackageName());
	}
}

查看AutoConfigurationPackages中的Registrar这个类的源码,在Registrar#registerBeanDefinitions方法中有这样一句代码new PackageImport(metadata).getPackageName(),查看PackageImport的构造器后不难发现,这里获取的是StandardAnnotationMetadata这个实例所在的包名。

/**
 * metadata: 实际上是 StandardAnnotationMetadata 实例。
 * metadata#getClassName(): 获取标注 @AutoConfigurationPackage 注解的类的全限定名。
 * ClassUtils.getPackageName(…): 获取其所在包。
 */
PackageImport(AnnotationMetadata metadata) {
	this.packageName = ClassUtils.getPackageName(metadata.getClassName());
}

此时再回去看Registrar#registerBeanDefinitions中调用的AutoConfigurationPackages#register方法

public static void register(BeanDefinitionRegistry registry, String... packageNames) {
	// BEAN:AutoConfigurationPackages类的全限定名
	// 此时判断BeanDefinitionRegistry中是否存在以BEAN作为beanName的BeanDefinition对象
	// 如果不存在,走else方法,构造了一个BackPackages实例,进行注册
	if (registry.containsBeanDefinition(BEAN)) {
		BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
		ConstructorArgumentValues constructorArguments = beanDefinition
				.getConstructorArgumentValues();
		constructorArguments.addIndexedArgumentValue(0,
				addBasePackages(constructorArguments, packageNames));
	} else {
		GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
		beanDefinition.setBeanClass(BasePackages.class);
		beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
		beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
		registry.registerBeanDefinition(BEAN, beanDefinition);
	}
}
2.1.2 Import(AutoConfigurationImportSelector.class)

它是利用AutoConfigurationImportSelector机制再来给容器中批量导入一些配置东西的,接下来带大家了解究竟导入了哪些内容。

/**
 * AutoConfigurationImportSelector类中存在一个叫selectImports的方法,就是我们到底要向容器中导入哪些
 * 内容,都会在这里进行扫描并导入。
 */
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
    // 判断EnableAutoConfiguration是否开启默认开启true
    if (!isEnabled(annotationMetadata)) {
        return NO_IMPORTS;
    }
    // 1.加载META-INF/spring-autoconfigure-metadata.properties 文件
    // 2.从中获取所有符合条件的支持自动装配的类
    // 自动配置类全名.条件=条件的值
    AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
        .loadMetadata(this.beanClassLoader);
    // 获取AutoConfigurationEntry
    AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,annotationMetadata);
    return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}

接下来重点看getAutoConfigurationEntry(annotationMetadata)方法,利用这个方法向容器中批量导入一些默认支持自动配置的类,当你理解了这部分内容之后,就基本了解了Spring Boot是如何进行自动装配的,废话不多说,让我们进入正题。

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    // 判断EnableAutoConfiguration是否开启默认开启true
    if (!isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    }
    // 获取注解属性
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    // 调用getCandidateConfigurations(annotationMetadata, attributes),利用loadSpringFactories(ClassLoader classLoader)加载当前系统所有的META-INF/spring.factories文件,得到默认支持的自动配置的类的列表
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    // 去除重复的 configuration
    configurations = removeDuplicates(configurations);
    // 获取到SpringBootApplication上exclude和excludeName配置的需要排除的类
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    // 检查configurations是否含有exclusions中的类
    checkExcludedClasses(configurations, exclusions);
    // 将exclusions中的类从configurations中排除
    configurations.removeAll(exclusions);
    // 对所有候选的自动配置类进行筛选,
    // 比如ConditionalOnProperty  当属性存在时
    // ConditionalOnClass  当class存在
    // ConditionalOnMissingClass  当这个clas不存在时才去配置
    // 过滤器
    configurations = getConfigurationClassFilter().filter(configurations);
    // 将自动配置的类,导入事件监听器,并触发fireAutoConfigurationImportEvents事件
	// 加载META-INF\spring.factories中的AutoConfigurationImportListener 
    fireAutoConfigurationImportEvents(configurations, exclusions);
    // 创建AutoConfigurationEntry对象
    return new AutoConfigurationEntry(configurations, exclusions);
}

标签:源码,Spring,Boot,自动装配,文件, 来源:

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。



这篇关于从源码中理解Spring Boot自动装配原理的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程