SpringBoot自动装配
Spring的自动装配机制简单来说就是将各种需要的Bean装配进框架容器中。
SpringBoot自动装配分为两类:
- 自动装配本项目Bean
- 自动装配依赖jar包下提供的bean
一起来看下SpringBoot是怎样加载的。很显然,SpringBoot项目是从main方法开始启动的,这个方法中做了什么呢?
在这里可以看到Spring将自身类加载进了容器中,这里跟spring加载配置类进容器一样。至于为什么加载这个类进容器就会自动装配好所有需要的bean信息,就是下面要说的。重点对象就是@SpringBootApplication。
我们来进入@SpringBootApplication看一下里面做了什么。
这里面重点对象包含三个:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
先来说一下@SpringBootConfiguration,@SpringBootConfiguration中包含了@Configuration,也就是说run()方法加载进去的是一个配置类,配置类中是可以对其他bean进行配置的。这就让这个启动类有了加载bean的权限。
而@ComponentScan就是将包下的所有Bean加载进容器,在这里就是将这个类所在的包下所有bean加载进容器。
所以为什么启动类一定要在项目根包下就不难理解了。这里加载的是本项目的bean。
看@EnableAutoConfiguration之前,我们先来看一下@Import注解的玩法
@Import也将bean加载进容器的注解,不同的是这个注解有三种加载方式,可以变着姿势对bean进行加载。只要思想不滑坡。
[@Import加载bean的三种方法:]((60条消息) @Import注解的作用_一直打铁的博客-CSDN博客_@import)
-
导入普通类
@Import({TestA.class}) @Configuration public class ImportConfig { }
-
导入一个实现了DeferredImportSelector接口的类
实现了ImportSelector接口的类会重写一个selectImports,这个方法会将返回的字符串数组中的bean加载进容器(重点)。这是唯一一种可以批量加载bean的。
@Import({SelfImportSelector.class}) @Configuration public class ImportConfig { }
public class SelfImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[]{"com.test.importdemo.TestC"}; } }
-
导入一个实现了ImportBeanDefinitionRegistrar接口的类
这个类会重写registerBeanDefinitions方法,在这个方法中可以对类进行加载
@Import({SelfImportBeanDefinitionRegistrar.class}) @Configuration public class ImportConfig { }
public class SelfImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { RootBeanDefinition root = new RootBeanDefinition(TestD.class); registry.registerBeanDefinition("testD", root); } }
了解完@Import的用法,让我们再回到@EnableAutoConfiguration注解中。
![image-20220423145432134](https://ling-28.oss-cn-beijing.aliyuncs.com/img/image-20220423145432134.png
@EnableAutoConfiguration中的注解@Import加载了一个AutoConfigurationImportSelector.class,让我们看一下这个类做了什么。从里面我看到了这个方法。很显然是用了@Import的第二种方法进行加载bean。具体加载了什么bean呢,再深入探探。
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
这是getAutoConfigurationEntry方法的具体实现:
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
//从jar包中加载class
// 这里获取class是从classpath下的jar包中的META-INF/spring.factories中读取到的
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
//过滤掉重复的class文件
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
依赖的jar包中有会在META-INF下包含spring.factories文件
在自动加载流程中会把EnableAutoConfiguration作为key,下面的class会以“,”隔开作为值,进行读取加载。注:这些class大多都会是jar包中提供给你的Configuration文件,Configuration中会给你提供对外的bean。你导入依赖后能调用的bean就是从这里来的。
菜鸟一个,错误的地方还望指出,拓展的知识希望大佬能跟我讲解一下。