一、引言
@SpringBootApplication 是 Spring Boot 框架中最核心的注解,它整合了多个 Spring 注解的功能,实现了 Spring Boot 的"约定大于配置"理念。理解 @SpringBootApplication 的底层实现原理,对于深入掌握 Spring Boot 框架至关重要。
本文将从源码层面深入解析 @SpringBootApplication 注解及其内部组合的各个注解,重点关注自动配置机制、组件扫描机制和配置类代理模式等核心内容。
二、@SpringBootApplication 注解概览
2.1 注解定义
java
@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 {};
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "nameGenerator")
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
}
2.2 核心组成
@SpringBootApplication 注解内部组合了三个核心注解:
- @SpringBootConfiguration:标识配置类
- @EnableAutoConfiguration:启用自动配置
- @ComponentScan:组件扫描
三、Java 元注解基础
在深入解析 Spring Boot 注解之前,需要先理解 Java 的四个原生元注解,它们是所有注解的基础。
3.1 @Target 注解
作用:限制「被标注的注解」能作用的位置(如类、方法、参数等)
取值 :ElementType 枚举,常用值:
- TYPE:类、接口、枚举
- METHOD:方法
- FIELD:成员变量
- PARAMETER:方法参数
- CONSTRUCTOR:构造器
- ANNOTATION_TYPE:其他注解(元注解的元注解)
源码:
java
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
ElementType[] value();
}
public enum ElementType {
TYPE, // 类、接口、枚举
FIELD, // 字段
METHOD, // 方法
PARAMETER, // 参数
CONSTRUCTOR, // 构造器
LOCAL_VARIABLE, // 局部变量
ANNOTATION_TYPE,// 注解
PACKAGE, // 包
TYPE_PARAMETER, // 类型参数(Java 8+)
TYPE_USE // 类型使用(Java 8+)
}
3.2 @Retention 注解
作用:定义「被标注的注解」的保留周期(即注解在什么阶段存在)
取值 :RetentionPolicy 枚举(优先级:SOURCE < CLASS < RUNTIME):
- SOURCE :仅源码阶段存在,编译后消失(如
@Override) - CLASS :编译后保留在
.class文件中,运行时不加载(默认值) - RUNTIME:保留到运行时,可通过反射获取(如 Spring 注解)
源码:
java
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
RetentionPolicy value();
}
public enum RetentionPolicy {
SOURCE, // 源码阶段
CLASS, // 类文件阶段
RUNTIME // 运行时阶段
}
3.3 @Documented 注解
作用 :当一个注解被 @Documented 标注时,该注解的使用信息会被 Javadoc 工具提取并生成到 API 文档中;若不加 @Documented,Javadoc 会忽略该注解的存在。
源码:
java
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}
3.4 @Inherited 注解
作用:允许「被标注的注解」被子类继承(仅作用于类级注解)
注意限制:
- 仅对
@Target(ElementType.TYPE)的注解有效(方法/字段的注解不继承) - 子类仅继承"父类的类级注解",不继承父接口的注解
源码:
java
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}
示例:
java
@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
}
@MyAnnotation
public class Parent {
}
// Child 类会自动继承 @MyAnnotation 注解
public class Child extends Parent {
}
四、@SpringBootConfiguration 注解详解
4.1 注解定义
java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
}
4.2 核心特性
@SpringBootConfiguration 是 Spring Boot 中特有的注解,是对 Spring 中 @Configuration 注解的拓展。@SpringBootConfiguration 内部承接了 @Configuration,关键的区别是 @SpringBootConfiguration 中有一个特有的参数:boolean proxyBeanMethods,默认的设置为 true。
4.3 proxyBeanMethods 参数详解
proxyBeanMethods 是布尔类型(默认值 true),取值决定 @SpringBootConfiguration 标注的配置类的工作模式:
4.3.1 Full 模式(proxyBeanMethods = true)
特点 :Spring 会为配置类生成 CGLIB 动态代理,@Bean 方法的调用会被代理拦截,确保每次调用都返回容器中已存在的单例 Bean(而非新创建实例)。
工作原理:
- Spring 使用 CGLIB 为配置类创建代理对象
- 当调用
@Bean方法时,代理会拦截调用 - 检查容器中是否已存在该 Bean,存在则返回,不存在则创建并注册
示例代码:
java
@SpringBootConfiguration(proxyBeanMethods = true) // 默认就是true,可省略
public class AppConfig {
// 定义Bean1
@Bean
public User user() {
return new User("张三", 20);
}
// 定义Bean2,依赖Bean1
@Bean
public Order order() {
// 直接调用本类的user()方法
return new Order(user()); // 实际返回容器中的单例User,而非新创建
}
}
// 测试:从容器中获取Bean
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(DemoApplication.class, args);
User user1 = context.getBean(User.class);
Order order = context.getBean(Order.class);
User user2 = order.getUser();
System.out.println(user1 == user2); // true(同一单例实例)
}
}
4.3.2 Lite 模式(proxyBeanMethods = false)
特点 :Spring 不会为配置类生成代理,@Bean 方法直接执行并返回新实例,容器仅负责注册 Bean(单例性由容器维护,但直接调用方法会绕开容器)。
工作原理:
- 配置类不会被代理,直接使用原始类
- 调用
@Bean方法时,直接执行方法体,创建新实例 - 容器中注册的 Bean 仍然是单例(通过容器获取时)
示例代码:
java
@SpringBootConfiguration(proxyBeanMethods = false)
public class AppConfig {
@Bean
public User user() {
return new User("张三", 20);
}
@Bean
public Order order() {
return new Order(user()); // 直接调用方法,返回新的User实例(非容器单例)
}
}
// 测试
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(DemoApplication.class, args);
User user1 = context.getBean(User.class); // 容器维护的单例
Order order = context.getBean(Order.class);
User user2 = order.getUser(); // 从Order中获取的是新实例(非容器单例)
System.out.println(user1 == user2); // false(两个不同实例)
}
}
4.3.3 两种模式的区别
关键理解:
- Full 模式 :直接调用
@Bean修饰的方法,也是从 Bean 容器中找到对应的 Bean 返回 - Lite 模式:直接创建一个新的实例,同时在 Bean 容器中,对应的 Bean 还是单例模式
困惑解答:那跟非单例模式有什么区别?
答案:单例特指的是在容器中获取 Bean 永远是这个 Bean,容器外部不属于单例模式的范围。也就是说:
- 通过容器获取的 Bean(
context.getBean())始终是单例 - 但在 Lite 模式下,配置类内部直接调用
@Bean方法会创建新实例,绕过了容器
4.3.4 @Configuration 源码分析
java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
@AliasFor(annotation = Component.class)
String value() default "";
boolean proxyBeanMethods() default true;
}
关键点:
@Configuration本身也是@Component的派生注解proxyBeanMethods参数控制是否使用代理模式- Spring 通过
ConfigurationClassPostProcessor处理配置类
五、@ComponentScan 注解详解
5.1 注解定义
java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
String resourcePattern() default "**/*.class";
boolean useDefaultFilters() default true;
ComponentScan.Filter[] includeFilters() default {};
ComponentScan.Filter[] excludeFilters() default {};
boolean lazyInit() default false;
}
5.2 @AliasFor 注解
作用 :@AliasFor 重定向,作用是指明两个参数的作用是一致的,可以互相替换。
示例:
java
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
使用参数 basePackages 设置扫描路径和使用 value 设置的效果是一样的,选一个来使用。
源码:
java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface AliasFor {
@AliasFor("attribute")
String value() default "";
@AliasFor("value")
String attribute() default "";
Class<? extends Annotation> annotation() default Annotation.class;
}
5.3 @ComponentScan 核心作用
5.3.1 扫描指定的包路径
- 在没有明确指定的前提下,默认是当前启动类所在的包路径,以及该包的所有子包
- 可以通过
basePackages或basePackageClasses指定扫描路径
5.3.2 注册 Bean
将扫描包下的所有类中带有 @Component(包括衍生注解 @Service、@Controller、@Repository 等)注解的所有类注册为 Bean,由 Spring 容器进行管理。
5.3.3 支持过滤规则
能够通过 includeFilter(包含指定的类)和 excludeFilter(过滤指定的类)进行过滤。
5.4 @SpringBootApplication 中的过滤配置
@SpringBootApplication 内部负载的 @ComponentScan 注解默认配置两个过滤类:
java
@ComponentScan(
excludeFilters = {
@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
),
@Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)
}
)
这是 Spring Boot 对「默认扫描规则」的补充------明确排除两类特殊类,避免它们被 @ComponentScan 误扫描并重复注册。
5.5 TypeExcludeFilter 详解
5.5.1 核心作用
提供开发者自定义排除逻辑的扩展点:允许通过代码(或配置)指定「不需要被 @ComponentScan 扫描注册的类」,Spring Boot 会自动委托该 Filter 执行排除逻辑。
5.5.2 工作原理
TypeExcludeFilter 是 Spring Boot 提供的「空实现基础类」,本身不包含固定排除规则,核心价值是「预留扩展接口」。
开发者可通过两种方式扩展其过滤逻辑:
- 手动实现
TypeExcludeFilter子类,重写match()方法定义排除规则 - 通过
SpringApplication.addTypeExcludeFilter(TypeExcludeFilter filter)动态添加排除规则 - 在
META-INF/spring.factories中配置自定义TypeExcludeFilter实现类(自动加载)
5.5.3 源码分析
java
public class TypeExcludeFilter implements TypeFilter, BeanFactoryAware {
private BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
if (this.beanFactory instanceof ListableBeanFactory && getClass() == TypeExcludeFilter.class) {
// 从容器中获取所有 TypeExcludeFilter 类型的 Bean
Collection<TypeExcludeFilter> delegates = ((ListableBeanFactory) this.beanFactory)
.getBeansOfType(TypeExcludeFilter.class).values();
for (TypeExcludeFilter delegate : delegates) {
if (delegate.match(metadataReader, metadataReaderFactory)) {
return true;
}
}
}
return false;
}
}
5.5.4 自定义示例
假设需要排除所有带有 @Deprecated 注解的类,不注册为 Bean:
java
// 1. 自定义 TypeExcludeFilter 实现类
public class DeprecatedExcludeFilter extends TypeExcludeFilter {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
// 逻辑:如果类上有 @Deprecated 注解,返回 true(表示排除)
return metadataReader.getAnnotationMetadata().hasAnnotation(Deprecated.class.getName());
}
}
// 2. 启动类中添加该过滤规则
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(DemoApplication.class);
// 添加自定义排除规则
application.addTypeExcludeFilter(new DeprecatedExcludeFilter());
application.run(args);
}
}
效果 :所有带 @Deprecated 的类,即使在扫描路径下,也不会被 @ComponentScan 注册为 Bean。
核心价值 :Spring Boot 不限制开发者的排除需求,通过 TypeExcludeFilter 预留了「灵活扩展」的入口,满足复杂场景下的自定义扫描规则。
5.6 AutoConfigurationExcludeFilter 详解
5.6.1 核心作用
排除 Spring Boot 的「自动配置类」,避免其被 @ComponentScan 误扫描并重复注册------这是 Spring Boot 自动配置机制的关键保障。
5.6.2 关键前提:自动配置类的加载逻辑
Spring Boot 的自动配置类(如 DataSourceAutoConfiguration、WebMvcAutoConfiguration),其加载方式不是 @ComponentScan 扫描,而是通过:
@EnableAutoConfiguration注解触发- 读取
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件(Spring Boot 2.7+ 版本) - 按需加载对应的自动配置类,注册为 Bean
这些自动配置类本身可能带有 @Configuration 注解(属于 @Component 的派生注解),如果不排除,会被 @ComponentScan 扫描到并「重复注册」------导致 Bean 冲突、配置覆盖等问题。
5.6.3 工作原理
AutoConfigurationExcludeFilter 会排除哪些类?
该 Filter 的 match() 方法会判断类是否满足以下条件,满足则排除(返回 true):
- 类上带有「自动配置相关注解」:
@AutoConfigureBefore、@AutoConfigureAfter、@AutoConfigureBetween、@AutoConfigureOrder、@AutoConfigureImport - 类属于 Spring Boot 自动配置类的「默认路径」(
org.springframework.boot.autoconfigure及其子包) - 类被包含在「自动配置类清单」中(即
AutoConfiguration.imports文件中声明的类)
简单说:所有 Spring Boot 官方的自动配置类,都会被这个 Filter 排除,不参与 @ComponentScan 的扫描注册。
5.6.4 源码分析
java
class AutoConfigurationExcludeFilter implements TypeFilter, BeanClassLoaderAware {
private ClassLoader beanClassLoader;
private volatile AutoConfigurationMetadata autoConfigurationMetadata;
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.beanClassLoader = classLoader;
}
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
return isConfiguration(metadataReader) && isAutoConfiguration(metadataReader);
}
private boolean isConfiguration(MetadataReader metadataReader) {
return metadataReader.getAnnotationMetadata().isAnnotated(Configuration.class.getName());
}
private boolean isAutoConfiguration(MetadataReader metadataReader) {
String className = metadataReader.getClassMetadata().getClassName();
// 检查是否在自动配置类清单中
return (this.autoConfigurationMetadata != null
&& this.autoConfigurationMetadata.wasProcessed(className))
|| AutoConfigurationImportSelector.getCandidateConfigurations(metadataReader, metadataReaderFactory) != null;
}
}
5.6.5 示例:避免自动配置类重复注册
假设 Spring Boot 的 DataSourceAutoConfiguration 类带有 @Configuration 注解:
-
如果没有
AutoConfigurationExcludeFilter:@ComponentScan会扫描到该类(若其路径在扫描范围内),并注册为 Bean;同时@EnableAutoConfiguration也会加载它,导致同一个类被注册两次,引发冲突。 -
有了该 Filter :
@ComponentScan会排除DataSourceAutoConfiguration,仅由@EnableAutoConfiguration按需加载,确保 Bean 唯一。
六、@EnableAutoConfiguration 注解详解
6.1 注解定义
java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLE_AUTOCONFIGURATION_EXCLUDE_PROPERTY = "spring.autoconfigure.exclude";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
6.2 核心作用
@EnableAutoConfiguration 导入了一个负责自动配置类的选择和加载的关键类:@Import({AutoConfigurationImportSelector.class})
Spring Boot 在启动时会通过 AutoConfigurationImportSelector 类来自动配置。这个类会读取 META-INF/spring.factories 文件中指定的自动配置类,然后根据条件决定是否应用它们。
6.3 AutoConfigurationImportSelector 详解
6.3.1 类定义
java
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
// ...
}
AutoConfigurationImportSelector 类实现了 DeferredImportSelector 接口,它的 selectImports 方法会返回所有需要被加载的自动配置类的全限定名。
6.3.2 selectImports 方法
java
@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 = getConfigurationClassFilter().filter(configurations);
// 触发自动配置导入事件
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
这个方法首先会从 META-INF/spring.factories 文件中加载自动配置类,然后从 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件中加载自动配置类,并返回合并后的自动配置类列表。
6.4 SpringFactoriesLoader 工具类
6.4.1 核心作用
AutoConfigurationImportSelector 是如何读取类路径中的 jar 包中的 spring.factories 文件?通过 SpringFactoriesLoader 工具类:
核心作用 :从 classpath 下所有 META-INF/spring.factories 文件中,根据指定的「接口/注解类型」,加载对应的「实现类/配置类全限定名列表」。
不止用于自动配置 :还能加载 ApplicationContextInitializer(上下文初始化器)、ApplicationListener(应用监听器)、Converter(类型转换器)等,是 Spring 生态中「SPI(服务发现)机制」的核心实现。
仅做"加载列表":不做筛选、不做导入,只负责读取文件内容,返回类名列表,交给调用者后续处理。
6.4.2 核心方法
java
public final class SpringFactoriesLoader {
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
List<String> factoryNames = loadFactoryNames(factoryType, classLoaderToUse);
if (logger.isTraceEnabled()) {
logger.trace("Loaded [" + factoryType.getName() + "] names: " + factoryNames);
}
List<T> result = new ArrayList<>(factoryNames.size());
for (String factoryName : factoryNames) {
result.add(instantiateFactory(factoryName, factoryType, classLoaderToUse));
}
AnnotationAwareOrderComparator.sort(result);
return result;
}
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
Map<String, List<String>> result = cache.get(classLoader);
if (result != null) {
return result;
}
result = new HashMap<>();
try {
// 加载所有 META-INF/spring.factories 文件
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
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();
String[] factoryImplementationNames =
StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
for (String factoryImplementationName : factoryImplementationNames) {
result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
.add(factoryImplementationName.trim());
}
}
}
// 替换所有列表,使其不可修改
result.replaceAll((k, v) -> Collections.unmodifiableList(v));
cache.put(classLoader, result);
} catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
return result;
}
}
例如 :传入 EnableAutoConfiguration.class,就返回所有 spring.factories 中 org.springframework.boot.autoconfigure.EnableAutoConfiguration= 对应的类名列表。
6.5 自动配置的核心流程
6.5.1 完整流程
-
触发入口 :用户应用标注
@SpringBootApplication,其内部包含@EnableAutoConfiguration注解 -
导入选择器 :
@EnableAutoConfiguration注解通过@Import(AutoConfigurationImportSelector.class),将该选择器注册到 Spring 容器 -
工具加载候选列表 :Spring 容器解析配置类时,调用
AutoConfigurationImportSelector.selectImports()方法,该方法内部直接调用SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, classLoader),读取所有META-INF/spring.factories中声明的自动配置类名(如RedisAutoConfiguration、DataSourceAutoConfiguration等) -
筛选与排序 :
AutoConfigurationImportSelector对候选列表做 3 件事:- 排除 :根据用户配置的
spring.autoconfigure.exclude或@EnableAutoConfiguration(exclude)剔除指定类 - 条件过滤 :通过
AutoConfigurationMetadata检查每个自动配置类的@Conditional注解(如@ConditionalOnClass检查依赖是否存在、@ConditionalOnMissingBean检查用户是否自定义了 Bean),剔除不满足条件的类 - 排序 :按
@AutoConfigureOrder注解或类名排序,确保依赖类先加载(如DataSourceAutoConfiguration先于JdbcTemplateAutoConfiguration)
- 排除 :根据用户配置的
-
导入容器 :
AutoConfigurationImportSelector将筛选后的最终列表返回给 Spring 容器,Spring 会将这些类作为@Configuration类解析,注册其内部定义的 Bean(如RedisTemplate、DataSource等)
6.5.2 条件注解示例
java
@Configuration
@ConditionalOnClass({RedisOperations.class})
@EnableConfigurationProperties(RedisProperties.class)
@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
public class RedisAutoConfiguration {
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
条件注解说明:
@ConditionalOnClass:当类路径中存在指定类时才生效@ConditionalOnMissingBean:当容器中不存在指定 Bean 时才创建
七、@AutoConfigurationPackage 注解详解
7.1 注解定义
java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
}
7.2 核心作用
@AutoConfigurationPackage 是 Spring Boot 自动配置机制的核心注解之一,核心作用是:将用户应用的「主程序所在包」注册为「自动配置包」,为后续自动配置类(如 MyBatis、Spring Data JPA 等)提供「默认扫描范围」------简单说,它是给自动配置类"划定边界",让自动配置能精准找到用户自定义的 Bean(如 @Controller、@Service、@Repository 等)。
7.3 归属和触发时机
@AutoConfigurationPackage 是 @SpringBootApplication 注解的组成部分(通过元注解组合),也就是说:当你在主程序类上标注 @SpringBootApplication 时,就默认启用了 @AutoConfigurationPackage。
触发时机 :和 @EnableAutoConfiguration 同步,在 Spring 容器解析 @SpringBootApplication 注解时,通过 @Import 加载 Registrar 组件,执行包路径注册。
7.4 实际应用场景
7.4.1 MyBatis 自动扫描 Mapper 接口
MyBatis 的自动配置类 MyBatisAutoConfiguration 中,会通过 @MapperScan 扫描 Mapper 接口,但如果用户没手动指定 basePackages,MyBatis 就会默认使用 @AutoConfigurationPackage 注册的包路径------所以你不用写 @MapperScan("com.example.demo.mapper"),只要 Mapper 接口和主程序在同一个包(或子包)下,就能被自动扫描到。
7.4.2 Spring Data JPA 自动扫描 Repository 接口
同理,Spring Data JPA 的 JpaRepositoriesAutoConfiguration 会默认扫描 @AutoConfigurationPackage 注册的包下的 @Repository 接口,无需手动配置扫描路径。
简单说 :@AutoConfigurationPackage 替你"告诉"所有自动配置类:用户的自定义组件在哪个包下,你直接去这儿扫就行!
7.5 工作原理:AutoConfigurationPackages.Registrar
7.5.1 Registrar 源码分析
java
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImports(metadata));
}
}
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
if (registry.containsBeanDefinition(BEAN)) {
BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues();
constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames));
} else {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(BasePackages.class);
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(BEAN, beanDefinition);
}
}
7.5.2 工作流程
-
获取主程序包路径 :Spring 启动时,
Registrar会通过AnnotationMetadata(注解元数据),获取到标注了@AutoConfigurationPackage的类(也就是你的主程序类,如DemoApplication)的包名(比如com.example.demo)。 -
注册包路径为 Spring Bean :
Registrar会将这个包名注册成一个特殊的 Spring Bean(类型为BasePackagesBeanDefinition),本质是把包路径"记录"到 Spring 容器中。 -
自动配置类读取包路径 :后续的自动配置类(如 MyBatis、JPA 的自动配置),会通过
AutoConfigurationPackages.get(ApplicationContext)方法,从 Spring 容器中获取到这个包路径,作为自己的默认扫描范围。
7.5.3 获取包路径的方法
java
public static List<String> get(BeanFactory beanFactory) {
try {
return beanFactory.getBean(BEAN, BasePackages.class).get();
} catch (NoSuchBeanDefinitionException ex) {
throw new IllegalStateException("Unable to retrieve @AutoConfigurationPackage base packages", ex);
}
}
八、@SpringBootApplication 完整执行流程总结
启动流程时序图
markdown
1. Spring Boot 应用启动
↓
2. 扫描 @SpringBootApplication 注解
↓
3. 解析 @SpringBootConfiguration
- 创建配置类代理(proxyBeanMethods=true)
↓
4. 解析 @ComponentScan
- 扫描指定包下的组件
- TypeExcludeFilter 排除自定义类型
- AutoConfigurationExcludeFilter 排除自动配置类
- 注册 @Component 及其派生注解的类为 Bean
↓
5. 解析 @EnableAutoConfiguration
↓
6. 执行 @AutoConfigurationPackage
- AutoConfigurationPackages.Registrar 注册主程序包路径
↓
7. 执行 AutoConfigurationImportSelector.selectImports()
- SpringFactoriesLoader 加载所有 spring.factories 中的自动配置类
- 条件注解过滤(@ConditionalOnClass 等)
- 排除用户指定的类
- 排序(@AutoConfigureOrder)
↓
8. Spring 容器加载筛选后的自动配置类
↓
9. 自动配置类注册 Bean 到容器
↓
10. 应用启动完成