继之前的文章SpringBoot核心注解之后,今天我们一起来分析一下SpringBoot的自动配置实现原理,表面上看起来深不可测,当我们分析过源码之后,会觉得它本身并没有那么复杂。
通过分析springboot源码,我们发现在@EnableAutoConfiguration注解中,@Import(Spring 提供的一个注解,可以导入配置类或者Bean到当前类中)导入了AutoConfigurationImportSelector类,根据名字来看,它应该就是我们要找到的目标了。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
Class<?>[] exclude() default {};
/**
* Exclude specific auto-configuration class names such that they will never be
* applied.
* @return the class names to exclude
* @since 1.3.0
*/
String[] excludeName() default {};
}
进入上面这个类,我们可以找到选择自动配置的主入口:
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(
autoConfigurationMetadata, annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
它调用了其他的几个方法来加载元数据等信息,最后返回一个包含许多自动配置类信息的字符串数组。
protected AutoConfigurationEntry getAutoConfigurationEntry(
AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
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;
}
这是的重点是:方法getCandidateConfigurations()返回了自动配置类的信息列表,而它通过调用SpringFactoriesLoader.loadFactoryNames()来扫描加载含有META-INF/spring.factories文件的jar包,该文件记录了具有哪些自动配置类。
接下来我们看看springboot中自动配置类的相关内容,我们选择一个具体的实践来看一下:
@Configuration
@ConditionalOnClass(Feign.class)
@EnableConfigurationProperties({FeignClientProperties.class, FeignHttpClientProperties.class})
public class FeignAutoConfiguration {
//....省略
}
我们可以看到对应的类的定义,其中@ConditionalOnClass注解是重点,该注解是@Conditional注解的一种。什么是@Conditional呢?
@Conditional是由Spring 4提供的一个新特性,用于根据特定条件来控制Bean的创建行为。而在我们开发基于Spring的应用的时候,难免会需要根据条件来注册Bean。
Spring框架提供了很多@Conditional供我们使用,主要有:
@ConditionalOnBean:仅仅在当前上下文中存在某个对象时,才会实例化一个Bean;
@ConditionalOnClass:某个class位于类路径上,才会实例化一个Bean;
@ConditionalOnExpression:当表达式为true的时候,才会实例化一个Bean;
@ConditionalOnMissingBean:仅仅在当前上下文中不存在某个对象时,才会实例化一个Bean;
@ConditionalOnMissingClass:某个class类路径上不存在的时候,才会实例化一个Bean;
@ConditionalOnNotWebApplication:不是web应用;
今天就简单分析到这里,更细节的内容大家可以再沿着这个思路,进一步分析源码。对于每一个技术实现,我们不仅要知其然,还要知其所以然。