SpringBoot自动装配原理分析

关于SpringBoot自动装配底层源码分析

PS:小白试手,理解有误可以评论指出,我会仔细斟酌

入手点:我们从启动类出发,其中有一个@SpringBootApplication的注解

@SpringBootApplication // springboot自动装配开启
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class);
    }
}

点进去看看这个注解,会发现这是一个组合注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
// 以下三个注解个人觉得比较重要
@SpringBootConfiguration // SpringBoot配置,内部其实还是一个@Configuration,也就是一个配置类
@EnableAutoConfiguration // 启动自动配置,也就是SpringBoot自动装配的启动
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication{...}

由此我们可以得出,SpringBoot自动装配一个重要的注解就是@EnableAutoConfiguration这个注解,我们点进去可以看到

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
// 相关的注解
@AutoConfigurationPackage // 自动配置包
@Import(AutoConfigurationImportSelector.class) // 引入了一个自动配置导入选择器
public @interface EnableAutoConfiguration {
	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
    ...
}

接下来我们不论执行的顺序,抓重点说明,先由@Import(AutoConfigurationImportSelector.class)这个引入的类入手;我们通过idea的一个执行日志可知,我们这个选择器先进的process()

在这里插入图片描述

然后进入一个getAutoConfigurationEntry()的方法,该方法的字面意思就是获取自动配置入口

/**
 * Return the {@link AutoConfigurationEntry} based on the {@link AnnotationMetadata}
 * of the importing {@link Configuration @Configuration} class.
 * @param autoConfigurationMetadata the auto-configuration metadata
 * @param annotationMetadata the annotation metadata of the configuration class
 * @return the auto-configurations that should be imported
 */
// 获取自动配置的入口
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
			AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
    	// 获取候选的配置
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
		...
	}

再由这个类中的getCandidateConfigurations()获取候选配置的方法,进入可知

/**
 * Return the auto-configuration class names that should be considered. By default
 * this method will load candidates using {@link SpringFactoriesLoader} with
 * {@link #getSpringFactoriesLoaderFactoryClass()}.
 * @param metadata the source metadata
 * @param attributes the {@link #getAttributes(AnnotationMetadata) annotation
 * attributes}
 * @return a list of candidate configurations
 */
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
				getBeanClassLoader());
    	// 其实根据这里的断言判断,我们可以知道,上面方法回去spring-factories中去加载我们的配置
		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;
	}

根据该类的断言判断我们可以知道该类会去加载一个在META-INF/目录下的一个spring.factories文件,但是我们还是进入loadFactoryNames()方法中

/**
 * Load the fully qualified class names of factory implementations of the
 * given type from {@value #FACTORIES_RESOURCE_LOCATION}, using the given
 * class loader.
 * 使用给定的类加载器从“ META-INF / spring.factories”加载给定类型的工厂实现的标准类名
 * @param factoryClass the interface or abstract class representing the factory
 * @param classLoader the ClassLoader to use for loading resources; can be
 * {@code null} to use the default
 * @throws IllegalArgumentException if an error occurs while loading factory names
 * @see #loadFactories
 */
	public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
		String factoryClassName = factoryClass.getName();
		return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
	}

进入方法后,我们通过文档注释已经很清楚了是从哪里加载,但是我们还是走到最后面看看,进入loadSpringFactories()方法中

public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
	String factoryClassName = factoryClass.getName();
	return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}

在进入loadSpringFactories()方法中

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
		MultiValueMap<String, String> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		try {
			Enumeration<URL> urls = (classLoader != null ?
					classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
         ...

这个方法中的FACTORIES_RESOURCE_LOCATION常量在该类的开头就有定义

在这里插入图片描述

然后该文件中存储的是我们需要导入的类的全限定名 spring.factories

在这里插入图片描述

最后再通过一个注解@EnableAutoConfiguration中的注解@AutoConfigurationPackage

在这里插入图片描述
这个注解中引入的 AutoConfigurationPackage这个类

在这里插入图片描述
再由Registrar类中的registerBeanDefinitions()方法去调用register()方法,对我们需要导入的类进行注册

@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    register(registry, new PackageImport(metadata).getPackageName());
}
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
	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);
	}
}

总结:

1️⃣:首先通过启动类上的组合注解@SpringBootApplication中得到注解@EnableAutoConfiguration

2️⃣:然后再由注解@EnableAutoConfiguration中引入的AutoConfigurationImportSelector知道,他会扫描spring-factories这个文件,读取需要配置的类的全限定名

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值