SpringBoot自动装配原理分析:AutoConfigurationImportSelector
1、注解@SpringBootApplication
进入启动类源码可以看到@SpringBootApplication注解是一个组合注解,他其实是由@SpringBootConfiguration 、
@EnableAutoConfiguration 、@ComponentScan 三个注解组合而成。
@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 {
···省略
}
1.1、@SpringBootConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {}
可以看到其实是一个被@Configuration 标记的配置类,与@Controller、@Service 一样,只不过区分了使用场景,本质上就是@Component ,标记为一个启动类。
1.2、@ComponentScan
spring的注解,用来开启注解扫描,其实就是限定扫描哪写被@Component 注解的类。
1.3、@EnableAutoConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}
可以看到两个比较重要的地方@AutoConfigurationPackage 以及@Import(AutoConfigurationImportSelector.class) ,主要的在AutoConfigurationImportSelector 类中,它实现了DeferredImportSelector ,而DeferredImportSelector 又实现了ImportSelector ,所以是ImportSelector 的一个实现类。
2、分析AutoConfigurationImportSelector
AutoConfigurationImportSelector 实现了ImportSelector 的方法selectImports ,之前会执行该方法,不过自从引入了DeferredImportSelector ,实现ImportSelector 的selectImports 再也没有执行了,而是转换成了由内部类AutoConfigurationGroup 的实现了DeferredImportSelector 的内部类Group 的两个方法process和selectImports作为入口(有点绕。。。。。)
2.1、分析process方法
@Override
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
1、获取所有的自动配置
getAutoConfigurationEntry():从META-INF/spring.factories获取
getAutoConfigurationMetadata():从META-INF/spring-autoconfigure-metadata.properties获取
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.
getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
这个方法重要的只有中间的getAutoConfigurationEntry 调用,获取所有的自动配置类全限定类名;以及getAutoConfigurationMetadata方法,该方法获取了所有的每个配置类要创建的前提条件,也就是@Condition。
2.2、getAutoConfigurationMetadata方法
private AutoConfigurationMetadata getAutoConfigurationMetadata() {
if (this.autoConfigurationMetadata == null) {
this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
}
return this.autoConfigurationMetadata;
}
protected static final String PATH = "META-INF/spring-autoconfigure-metadata.properties";
static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
return loadMetadata(classLoader, PATH);
}
static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {
try {
Enumeration<URL> urls = (classLoader != null) ? classLoader.getResources(path)
: ClassLoader.getSystemResources(path);
Properties properties = new Properties();
while (urls.hasMoreElements()) {
读取文件中的配置条件信息
properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource(urls.nextElement())));
}
return loadMetadata(properties);
}
}
META-INF/spring-autoconfigure-metadata.properties 该文件已经缓存了要创建配置类的前提条件,后续检查是否需要创建该配置类就是从该文件中去获取的。
举例:
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration.ConditionalOnClass=javax.servlet.ServletRequest
意思是说:ServletWebServerFactoryAutoConfiguration类上的@ConditionalOnClass注解的条件是javax.servlet.ServletRequest
2.3、重点:getAutoConfigurationEntry方法
重点重点重点!重要的事情说三遍!
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
1、查看是否开启了自动配置,默认是true
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
2、获取注解属性,也就是@EnableAutoConfiguration内的两个exclude和excludeName属性
AnnotationAttributes attributes = getAttributes(annotationMetadata);
3、从META-INF/spring.factories获取所有的候选配置
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
4、移除重复的,就是用Set不可重复性质。
configurations = removeDuplicates(configurations);
5、获取排除的配置
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
6、检验配置,其实就是排除的配置项在类加载器上但是不在候选者里面则抛出异常
checkExcludedClasses(configurations, exclusions);
7、删除排除的配置项
configurations.removeAll(exclusions);
8、过滤,这儿过滤一般就是@Condintion注解进行过滤,单独讲
configurations = filter(configurations, autoConfigurationMetadata);
9、事件发布
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
大流程来说还是比较简单的,较难的是里面的细节问题,需要分析@Condintion是如何过滤的。
2.4、getCandidateConfigurations方法
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
return configurations;
}
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
1、就是注解类名称,org.springframework.boot.autoconfigure.EnableAutoConfiguration
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
1、先从缓存中获取与,spring的一贯做法,不在解释
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
2、FACTORIES_RESOURCE_LOCATION的值是:META-INF/spring.factories
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
3、放到缓存当中,下次就不需要再次解析了。
cache.put(classLoader, result);
return result;
}
}
该方法作用就是读取所有的候选配置,也就是从META-INF/spring.factories 中读取所有的配置,最后封装成一个Map返回。
2.4、总结
- 首先通过
getAutoConfigurationMetadata() 方法读取spring-autoconfigure-metadata.properties 获得创建配置类的前提条件,封装到AutoConfigurationMetadataLoader的properties属性上。 - 着手读取spring.factories文件获取自动装配类集合。
- 读取@EnableAutoConfiguration的属性,exclude和excludeName。
- 去重,检查自动装配的Class排除的集合是否合法。
- 移除掉所有的exclude和excludeName中的名单
- 再次过滤通过@Condintioni条件进行过滤。
3、过滤自动配置filter
private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {
long startTime = System.nanoTime();
String[] candidates = StringUtils.toStringArray(configurations);
boolean[] skip = new boolean[candidates.length];
boolean skipped = false;
1、获取自动配置过滤器
for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
invokeAwareMethods(filter);
2、判断是否满足条件,否则后续需要移除掉。
boolean[] match = filter.match(candidates, autoConfigurationMetadata);
for (int i = 0; i < match.length; i++) {
3、为false则需要移除,代表不满足条件
if (!match[i]) {
skip[i] = true;
candidates[i] = null;
skipped = true;
}
}
}
if (!skipped) {
return configurations;
}
List<String> result = new ArrayList<>(candidates.length);
for (int i = 0; i < candidates.length; i++) {
3、只保留skip为false的,返回过滤后的配置类
if (!skip[i]) {
result.add(candidates[i]);
}
}
return new ArrayList<>(result);
}
3.1、getAutoConfigurationImportFilters方法
AutoConfigurationImportFilter 同样也在META-INF/spring.factories 下面,如下所示
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
最后会返回这三个condition的类。
protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
}
public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
1、与前面获取配置文件内容类似,就是读取配置文件获得全限定类名
List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);
List<T> result = new ArrayList<>(factoryImplementationNames.size());
for (String factoryImplementationName : factoryImplementationNames) {
2、通过反射进行实例化对象
result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));
}
AnnotationAwareOrderComparator.sort(result);
return result;
}
3.2、match方法解析
以OnClassCondition为例,他继承了FilteringSpringBootCondition。继承关系如下所示:
首先进入父类FilteringSpringBootCondition的match方法:
@Override
public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) {
ConditionEvaluationReport report = ConditionEvaluationReport.find(this.beanFactory);
1、ConditionOutcome中属性match为ture就保留,反之移除。
ConditionOutcome[] outcomes = getOutcomes(autoConfigurationClasses, autoConfigurationMetadata);
boolean[] match = new boolean[outcomes.length];
for (int i = 0; i < outcomes.length; i++) {
2、如果outcome为null代表该配置类上没有@OnClassCondition注解
若存在则看match是否为true,满足这两个条件任意一个就保留。
match[i] = (outcomes[i] == null || outcomes[i].isMatch());
if (!match[i] && outcomes[i] != null) {
3、事件发布
logOutcome(autoConfigurationClasses[i], outcomes[i]);
if (report != null) {
report.recordConditionEvaluation(autoConfigurationClasses[i], this, outcomes[i]);
}
}
}
return match;
}
getOutcomes方法返回了一个包装类,若不为null,则根据内部包装类的match属性判断是否满足条件,若为null则说明该配置类上没有@Condition注解。
3.3、getOutcomes方法解析
@Override
protected final ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses,
AutoConfigurationMetadata autoConfigurationMetadata) {
1、看cpu核心数量,是否满足多线程条件,多开一个线程解析。
if (Runtime.getRuntime().availableProcessors() > 1) {
2、将配置类数量分成两个数组,由两个线程解析最终还是调用了resolveOutcomes方法
return resolveOutcomesThreaded(autoConfigurationClasses, autoConfigurationMetadata);
}
else {
2、否则单线程解析
OutcomesResolver outcomesResolver = new StandardOutcomesResolver(autoConfigurationClasses, 0,
autoConfigurationClasses.length, autoConfigurationMetadata, getBeanClassLoader());
return outcomesResolver.resolveOutcomes();
}
}
3.4、resolveOutcomes方法
public ConditionOutcome[] resolveOutcomes() {
return getOutcomes(this.autoConfigurationClasses, this.start, this.end, this.autoConfigurationMetadata);
}
private ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses, int start, int end,
AutoConfigurationMetadata autoConfigurationMetadata) {
ConditionOutcome[] outcomes = new ConditionOutcome[end - start];
1、遍历从spring.factory文件中读取的配置类
for (int i = start; i < end; i++) {
String autoConfigurationClass = autoConfigurationClasses[i];
if (autoConfigurationClass != null) {
2、获取当前的要创建类的前提条件
String candidates = autoConfigurationMetadata.get(autoConfigurationClass, "ConditionalOnClass");
if (candidates != null) {
3、最终匹配,查看该类是否存在
outcomes[i - start] = getOutcome(candidates);
}
}
}
return outcomes;
}
private ConditionOutcome getOutcome(String candidates) {
try {
1、只有前提一个条件
if (!candidates.contains(",")) {
return getOutcome(candidates, this.beanClassLoader);
}
2、有多个前提条件,只要是一个不满足就返回
for (String candidate : StringUtils.commaDelimitedListToStringArray(candidates)) {
3、该方法若outcome不为null,返回的一定是不满足,为null说明该配置类没有条件或者条件满足
ConditionOutcome outcome = getOutcome(candidate, this.beanClassLoader);
if (outcome != null) {
return outcome;
}
}
}
return null;
}
private ConditionOutcome getOutcome(String className, ClassLoader classLoader) {
1、若返回true说明条件类不存在,将match设置为false
若返回false说明创建条件类成功
if (ClassNameFilter.MISSING.matches(className, classLoader)) {
return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnClass.class)
.didNotFind("required class").items(Style.QUOTE, className));
}
2、返回true说明条件类存在,或者配置类上没有@Condintion条件信息
return null;
}
@Override
public boolean matches(String className, ClassLoader classLoader) {
return !isPresent(className, classLoader);
}
static boolean isPresent(String className, ClassLoader classLoader) {
if (classLoader == null) {
classLoader = ClassUtils.getDefaultClassLoader();
}
try {
1、真正判断条件的地方。实例化@CondintionOnClass指向的类,若类存在则通过反射创建对象,否则抛出异常,返回false。
resolve(className, classLoader);
return true;
}
catch (Throwable ex) {
return false;
}
}
该方法就是去真正的对比是否满足条件,具体的在isPresent 方法处,通过实例化condintion来判断是否满足配置类创建的前提条件,若实例化成功则满足条件,否则配置类将被过滤。
3.5、总结
filter方法的大致流程为:
-
获得AutoConfigurationImportFilter ,该类被OnBeanCondition、OnclassCondition、OnWebApplicationCondition 三个类实现,进而转化为三各类各自的解析,进入match 方法 -
通过预先已经在META-INF/spring-autoconfigure-metadata.properties 文件中设置好的配置类的前提条件,若条件为null,则说明该配置类不需要前提条件,反之获得Condition的全限定类名,然后通过反射机制创建实例,观察是否能创建成功,若创建成功说明满足配置类的创建条件,否则配置类将被过滤。具体的代码在这儿 match[i] = (outcomes[i] == null || outcomes[i].isMatch()); outcomes为null或者isMatch为true,说明配置类无需前提条件或条件满足。
4、总结
总的来说AutoConfigurationImportSelector的读取自动配置的流程为:
- 首先从
spring-autoconfigure-metadata.properties 读取创建配置类的前提条件。 - 从
spring.factories 文件中读取所有自动配置类,封装成集合。 - 读取
@EnableAutoConfiguration 的属性也就是exclude和excludeName 。 - 校验候选者是否合法,也就是在排除集合中但是不在自动配置候选者中的抛出异常。
- 从候选者中剔除排除的集合。
- 再次过滤,通过@Condintion判断前提条件是否满足。
|