关于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
这个文件,读取需要配置的类的全限定名