基于Springboot 2.2.4.RELEASE、jdk8
前言
最近在进行spring cloud alibaba的学习,由于cloud是建立在boot之上的,所以决定先把一知半解的boot原理弄懂,其中最重要的便是“自动装配”这个概念。boot这个巧妙的设计,让java程序员脱离了各种xml配置的苦海,那它是如何在程序启动的时候,自动装载我们需要的bean到容器里面的呢?源码里有一切我们想要的答案~~~
先导知识
要想了解boot自动装配的实现,得先了解下几个注解。方便后面阅读源码。
1. @ConfigurationProperties
标有此注解的类的所有属性,会自动和配置文件中的配置项进行绑定(默认从全局配置文件中获取配置值)。
如配置@ConfigurationProperties(prefix="spring.redis"),则表示此类中的属性默认从配置文件中的spring.redis下对应配置的属性值获取。
2.@Import
将符合条件的的bean,注入到IoC容器中。详细可参考https://mp.weixin.qq.com/s/dNOBwMPHKdccmeJFWzzTOg
在spring4.2之前只支持导入配置类。在4.2之后支持导入普通的java类,并将其声明成一个bean。
Import的3种使用方式分别是:
-
直接导入普通java类
@Import({User.class})
-
配合自定义的ImportSelector使用,只有当条件满足时才注入bean
@Import({MyUserSelector.class})
-
配合ImportBeanDefinitionRegistrar使用
在第一点中,直接把类注入到IOC容器中,只能调用类的无参构造函数,如果想对类进行个性化定制,就可以用此方式,手动将bean注册到容器中(需配合@Configuration使用)。
3.@Conditional
该注解可以实现满足一定条件时,才启用配置。
常用的一些扩展注解有:
ConditionalOnBean :容器中存在指定 Bean,则生效。
ConditionalOnMissingBean:容器中不存在指定 Bean,则生效。
ConditionalOnClass: 系统中有指定的类,则生效。
ConditionalOnMissingClass: 系统中没有指定的类,则生效。
ConditionalOnProperty: 系统中指定的属性是否有指定的值。
ConditionalOnWebApplication: 当前是web环境,则生效。
自动装配的实现
自动装配在springboot中是通过@EnableAutoConfiguration注解来开启的,在启动类的注解@SpringBootApplication中就声明了这个注解。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
//...
}
这里对@Enable注解进行一下说明,在spring3.1版本就已经开始支持@Enable注解了,主要作用就是把相关组件的Bean装配到IoC容器中。在此之前,使用基于JavaConfig的形式来完成Bean的装载,则必须使用@Configuration及@Bean。而@Enable本质上是针对这两个注解的封装,所以不难发现@Enable系列的注解都会携带一个@Import注解,Spring在解析到@Import注解的时候,会根据Import导入的配置类来实现Bean的装配。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
//...
}
AutoConfigurationImportSelector类实现了ImportSelector接口,它只有一个selectImports的抽象方法,返回一个string数组,这个数据返回的类最终会被装配到IoC容器中。和@Configruation不同的是,ImportSelector可实现批量装配,并且还可以通过逻辑处理来选择要装配的类,也就是说可以根据上下文来决定哪些类能被IoC容器初始化。
public interface ImportSelector {
String[] selectImports(AnnotationMetadata importingClassMetadata);
@Nullable
default Predicate getExclusionFilter() {
return null;
}
}
接下来看一下AutoConfigurationImportSelector.class的具体实现。先进入selectImports方法,此方法也是ImportSelector的核心方法,主要用于批量获取需要注入的配置类。
//主要看这个方法
@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;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//获取候选的配置类信息
List configurations = getCandidateConfigurations(annotationMetadata, attributes);
//进行一系列过滤判断,得到最终要返回的配置信息
configurations = removeDuplicates(configurations);
Set exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
getCandidateConfigurations会调用SpringFactoriesLoader.loadFactoryNames去META-INF/spring.factories配置文件下获取key为EnableAutoConfiguration的配置信息回来。
protected List getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
//getSpringFactoriesLoaderFactoryClass()获取回来的正是EnableAutoConfiguration,在loadFactoryNames会从META-INF/spring.factories配置中加载回来的map中,找到key为EnableAutoConfiguration的配置类作为候选返回list。
List configurations =
SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
//默认从META-INF/spring.factories拿配置,体现了springboot的核心思想“约定优于配置”。
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;
}
SpringFactoriesLoader.loadFactoryNames就是实际去配置文件META-INF/spring.factories获取所有配置类信息回来的方法。
public static List loadFactoryNames(Class> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
//getOrDefault(factoryTypeName, Collections.emptyList())获取的就是key为EnableAutoConfiguration的map
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map> loadSpringFactories(@Nullable ClassLoader classLoader) {
//...
try {
//这里就是实际获取META-INF/spring.factories路径的地方
Enumeration urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
//....
return result;
}
//...
}
以下是源码走读的顺序图
参考博文
https://juejin.im/post/5ce5effb6fb9a07f0b039a14#heading-11