@SpringBootApplication 是 Spring Boot 提供的一个便捷注解,用于简化配置和启动 Spring Boot 应用。它实际上是一个组合注解,包含了几个关键的注解。以下是对 @SpringBootApplication 及其底层实现的详细解析。
1. @SpringBootApplication 注解
@SpringBootApplication 本质上是一个组合注解,它包含了以下三个核心注解:
@EnableAutoConfiguration@ComponentScan@Configuration
@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 {
@AliasFor(annotation = EnableAutoConfiguration.class)
Class<?>[] exclude() default {};
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default {};
}
2. @SpringBootConfiguration
@SpringBootConfiguration 是一个特殊的 @Configuration 注解,用于表明这是一个 Spring Boot 配置类。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
3. @EnableAutoConfiguration
@EnableAutoConfiguration 告诉 Spring Boot 根据项目中的依赖自动配置 Spring 应用上下文。这是 Spring Boot 能够简化配置的重要原因。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
@AliasFor("exclude")
Class<?>[] exclude() default {};
@AliasFor("excludeName")
String[] excludeName() default {};
}
@EnableAutoConfiguration 使用 @Import(AutoConfigurationImportSelector.class) 导入了 AutoConfigurationImportSelector 类,该类负责从类路径下查找自动配置类并导入它们。
AutoConfigurationImportSelector.java
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, EnvironmentAware, Ordered {
// Implementation details
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
// Resolve and load auto configuration classes
}
// Other methods and implementation details
}
4. @ComponentScan
@ComponentScan 扫描指定包及其子包中的组件(如 @Component、@Service、@Repository 和 @Controller),并将其注册到 Spring 容器中。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(ComponentScans.class)
@Inherited
public @interface ComponentScan {
String[] value() default {};
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
// Other attributes and filters
}
总结
@SpringBootApplication 通过组合多个注解简化了 Spring Boot 应用的配置和启动过程:
@SpringBootConfiguration:标识配置类。@EnableAutoConfiguration:启用自动配置机制,根据类路径下的依赖自动配置 Spring 应用上下文。@ComponentScan:自动扫描指定包及其子包中的组件,并将其注册到 Spring 容器中。
AutoConfigurationImportSelector实现流程
AutoConfigurationImportSelector 负责在应用启动时自动加载和注册符合条件的自动配置类。通过 @EnableAutoConfiguration 注解,我们可以启用自动配置功能,而 AutoConfigurationImportSelector 这个类具体实现了自动配置类的选择和导入过程。
以下是 AutoConfigurationImportSelector 的详细实现流程:
1. @EnableAutoConfiguration 注解
@EnableAutoConfiguration 注解中使用了 @Import(AutoConfigurationImportSelector.class) 来导入 AutoConfigurationImportSelector 类:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
// ...
}
2. selectImports 方法
AutoConfigurationImportSelector 实现了 DeferredImportSelector 接口,该接口定义了 selectImports 方法,用于返回需要导入的配置类名列表。
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, EnvironmentAware, Ordered {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
protected AutoConfigurationEntry getAutoConfigurationEntry(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);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
// 其他方法及实现细节
}
3. getCandidateConfigurations 方法
getCandidateConfigurations 方法从 META-INF/spring.factories 文件中读取所有候选的自动配置类:
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;
}
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
SpringFactoriesLoader.loadFactoryNames 方法会从 META-INF/spring.factories 文件中读取 EnableAutoConfiguration 对应的配置类名列表。
4. 过滤与排除
获取到候选配置类后,AutoConfigurationImportSelector 会进行过滤和排除不需要的配置类:
- 去重处理:调用
removeDuplicates方法。 - 排除指定类:调用
getExclusions和checkExcludedClasses方法,根据注解配置排除某些类。 - 条件过滤:调用
filter方法,根据环境和条件进行过滤。
5. 发布事件
在最终确定要导入的配置类列表后,会发布 AutoConfigurationImportEvent 事件,通知相关监听器:
protected void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
if (!listeners.isEmpty()) {
AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
for (AutoConfigurationImportListener listener : listeners) {
listener.onAutoConfigurationImportEvent(event);
}
}
}
6. 总结
AutoConfigurationImportSelector 实现了自动配置类的选择和导入过程,具体步骤如下:
- 读取元数据:读取
EnableAutoConfiguration注解的属性。 - 查找候选类:从
META-INF/spring.factories文件中读取所有候选的自动配置类。 - 去重和排除:对候选配置类进行去重、排除等处理。
- 条件过滤:根据环境和条件过滤不适用的配置类。
- 返回结果:返回最终确定的自动配置类列表,供 Spring 容器导入
SpringFactoriesLoader.loadFactoryNames如何读取配置类名
SpringFactoriesLoader 是 Spring 框架中的一个辅助类,用于从 META-INF/spring.factories 文件中加载工厂类名。这个文件中通常包含一些关键组件或配置类的映射关系。了解 SpringFactoriesLoader.loadFactoryNames 的实现有助于理解 Spring Boot 自动配置机制的底层原理。
SpringFactoriesLoader 类
首先,我们来看一下 SpringFactoriesLoader 类的核心方法 loadFactoryNames:
public abstract class SpringFactoriesLoader {
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentHashMap<>();
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
MultiValueMap<String, String> result = cache.get(classLoaderToUse);
if (result != null) {
return result;
}
try {
Enumeration<URL> urls = classLoaderToUse.getResources(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 factoryClassName = ((String) entry.getKey()).trim();
for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryClassName, factoryName.trim());
}
}
}
cache.put(classLoaderToUse, result);
return result;
} catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
}
实现步骤详细解析
-
常量定义:
FACTORIES_RESOURCE_LOCATION定义了默认的位置META-INF/spring.factories,用来存放配置文件。
-
缓存机制:
- 使用
cache以ClassLoader为键进行缓存,避免重复读取和解析配置文件,提高性能。
- 使用
-
加载工厂类名:
loadFactoryNames方法根据传入的factoryClass类型,返回对应的工厂类名列表。
-
加载 Spring 工厂:
loadSpringFactories方法用于加载并解析spring.factories配置文件中的内容。
-
获取资源:
classLoaderToUse.getResources(FACTORIES_RESOURCE_LOCATION)获取所有的META-INF/spring.factories文件(可能存在多个)。
-
读取和解析资源:
- 遍历所有找到的
URL资源,通过PropertiesLoaderUtils.loadProperties将其加载为Properties对象。 - 遍历
Properties中的每一条记录,将 key 和 value 作为工厂类名和对应的实现类名。
- 遍历所有找到的
-
结果存储:
- 将解析后的结果存储在
MultiValueMap中,并缓存到内存中。
- 将解析后的结果存储在
示例文件 META-INF/spring.factories
典型的 META-INF/spring.factories 文件内容如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
...
此文件表示 EnableAutoConfiguration 对应了一系列自动配置类。
加载过程简化步骤
- 调用
loadFactoryNames方法,传入EnableAutoConfiguration.class和ClassLoader。 - 检查缓存,如果缓存中已有则直接返回结果。
- 获取资源,找到所有路径下的
META-INF/spring.factories文件。 - 解析文件,将文件内容加载为
Properties对象并遍历每个键值对。 - 存储结果,将解析结果存储到
MultiValueMap并缓存。 - 返回结果,根据传入的工厂类名查找并返回对应的实现类