我们都直到 Spring Boot 通过一些注解就可以完成一些功能的配置,如:只需要简单的在 application.yml 中配置一些 Redis 的相关参数就可以使用 redis 了,这些是如何做到的呢?
@SpringBootApplication
我们先看 @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 {
// ...
}
我们看到了一个 @EnableAutoConfiguration 这个注解,从名字就可以看出,这个是允许自动配置的意思,我们继续看这个注解又干了什么事?
@EnableAutoConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class) // 利用 @Import 导入 AutoConfigurationImportSelector.class
public @interface EnableAutoConfiguration {
// ...
}
这个包上有一个注解 @Import(AutoConfigurationImportSelector.class),我们都知道 @Import 是导入一个类到 Spring 容器中,这里我们可以看到,SpringBoot 把 AutoConfigurationImportSelector.class 类加载到了容器里,那这个类有什么特殊的地方呢?
AutoConfigurationImportSelector.class
我们看一下 AutoConfigurationImportSelector 这个类的定义
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
// ...
}
AutoConfigurationImportSelector 实现了 DeferredImportSelector 接口,而 DeferredImportSelector 接口又继承了 ImportSelector 接口,接下来我们看一下 ImportSelector 接口主要是做什么的
ImportSelector 接口
ImportSelector 接口只有一个方法 selectImports
public interface ImportSelector {
/**
* Select and return the names of which class(es) should be imported based on
* the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
*/
String[] selectImports(AnnotationMetadata importingClassMetadata);
}
通过注释我们可以知道,这个类主要就是返回一个 String 数组,数组里是需要加入到 Spring 容器的全类名,也就是说,我们只要在数据中添加 com.example.Xxx 类,Spring 就会将这个 com.example.Xxx 类加入到 Spring 容器中,那么 SpringBoot 会返回什么样的类出来呢?
我们看下 AutoConfigurationImportSelector 是如何实现这个接口方法的
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
我们继续跟 getAutoConfigurationEntry 方法,会发现 Spring 会读取 META-INF\spring.factories 里的所有类,将其转换为 String 数组返回给 Spring 容器
spring.factories 文件
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener
# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\