用源码方式打开SpringBoot的自动装配原理
什么是装配?
学到SpringBoot的时候,有很多时候都忘记了在Spring会做的一件繁琐的事情,那就是把Bean对象托管到Spring Ioc容器,这个过程叫做装配,装配要写大量的xml文件,所以Spring被称为 配置地狱 。随后SpringBoot就是天降猛男,又来拯救程序员了,自动装配就是SpringBoot的核心之一。
那么它怎么自动的?
又到了最激动人心的源码环节了,首先来看看主启动类(xxxApplication)。
可以看到 @SpringBootApplication 这个注解,外表看不出有什么,一个自定义的注解,继续深入。
前面四个注解都是java原生注解,感兴趣可以去了解,不是自动装配的核心。
- @SpringBootConfiguration :这个注解是JavaConfig,其实就是一个IoC容器的配置类,这点也不是凭空捏造的,点进这个注解就会发现其有 @Configuration ,标注了@Configuration的Java类定义都是一个JavaConfig配置类。而在这个配置类中,任何标注了@Bean的方法,它的返回值都会作为Bean定义注册到Spring的IOC容器,方法名默认成为这个bean的id。
- @EnableAutoConfiguration :这个注解才是自动装配的核心,
@Enable*
开头的,一般都表示开启某项功能。
撕开发现也仅仅只有两个注解 @AutoConfigurationPackage 和 @Import(AutoConfigurationImportSelector.class)
@AutoConfigurationPackage :也不是自动装配的核心,值得注意的是在SpringBootApplication注解上有 @ComponentScan 的注解,即扫描指定包下的组件,这里不绕弯子了,这个就是扫描与主启动类同级目录下的所有包,扫完后 @AutoConfigurationPackage 来注册。
那么说说最核心的地方 @Import(AutoConfigurationImportSelector.class) 。
@Import的作用
先了解以下@Import,在Spring的时候,还记得用处是把分开的IOC容器合并在一起,那么@Import也简单明了,就是一起加载被合并的容器。
那么深入 AutoConfigurationImportSelector 这个配置类
先要明确一点,那么就是AutoConfigurationImportSelector继承了ImportSelector接口,ImportSelector接口只有ImportSelector方法,该方法返回一个数组这个实例,在配置类中,返回值都会被Spring IOC容器托管。所有看看这个方法返回的是什么,到底装着什么东西。
该接口文档上说的明明白白,其主要作用是收集需要导入的配置类,selectImports()方法的返回值就是我们向Spring容器中导入的类的全类名
毫无疑问,要想知道返回值,那么就跟着它走,进入 getAutoConfigurationEntry() 方法,该方法返回是要导入的类的条目。
冷静分析,我们只关注返回值,返回的是什么,显而易见return new AutoConfigurationEntry(configurations, exclusions);
exclusons是排除,所以不加理睬,关注configurations即可,可以看出List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
这里有我们想要的configurations,但是调用了getCandidateConfigurations()方法,继续跟进去。
跟到此处差不多快结束了,我们依旧关注返回值return configurations;
,然后就可以知道返回值在哪定义的
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
那么继续跟进到 SpringFactoriesLoader.loadFactoryNames() 方法中。
到这里,已经是真相大白了,loadFactoryNames()方法调用了下面的loadSpringFactories(),我们依旧关注返回值,result,我们就不多读源码了,我们正中靶心,找到加载了什么资源,放到了返回值中。
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
由此可得,是从这常量中获取到的资源,点进常量
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
发现其指定了一个地址,顺藤摸瓜
最终在该处找到了此资源,打开资源
全是我们需要装配的配置类,原来就是从自处读取来自动装配的。
总结
SpringBoot所有的自动配置都在启动时扫描加载,所有自动配置类都在META-INF/spring.factories中,但是却不一定生效,因为有的需要导入了相应的启动器依赖,才会生效,则点SpringBoot会有判断条件,这样做的目的是有效的减少@configuration类的数量从而降低SpringBoot的启动时间!所以只有导入相应的start启动器,自动装配才会生效,自动装配该启动器需要的配置类,会将所有导入的组件,以类名的方式返回,组件就会被添加到容器中。