title: @SpringBootApplication 注解 description: 深度解析 @SpringBootApplication 注解的组成,以及它与 @Configuration、@EnableAutoConfiguration、@ComponentScan 的关系。

@SpringBootApplication 注解

候选人小方在面试字节 P6 时,面试官问道:

"@SpringBootApplication 注解里面包含哪些注解?"

小方说:"有 @EnableAutoConfiguration..."

面试官追问:"还有呢?"

小方说:"还有...@ComponentScan?"

面试官:"那 @SpringBootConfiguration 呢?它和 @Configuration 有什么区别?"

小方答不上来了。

【面试官心理】 这道题我用来测试候选人对 Spring Boot 注解体系的理解程度。@SpringBootApplication 是 Spring Boot 应用的入口注解,能说清它由哪些注解组成、各自的作用、以及为什么这么设计,是理解 Spring Boot 的基础。


一、核心问题 🔴

1.1 问题拆解

第一层:组成

  • "@SpringBootApplication 包含哪些注解?"
  • "@SpringBootConfiguration 和 @Configuration 是什么关系?"

第二层:各自作用

  • "@EnableAutoConfiguration 是干什么的?"
  • "@ComponentScan 默认扫描哪些包?"

第三层:配置属性

  • "@SpringBootApplication 有哪些可配置属性?"
  • "exclude 属性是做什么的?"

1.2 标准回答

P5 回答:注解组成

@SpringBootApplication
// 等价于
@Configuration
@EnableAutoConfiguration
@ComponentScan

1.3 追问升级

追问 1:@SpringBootConfiguration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Configuration
public @interface SpringBootConfiguration {
    // @SpringBootConfiguration 就是 @Configuration
    // 只是换了个名字,用于区分 Spring Boot 配置和普通配置
}

所以:

@SpringBootApplication
public class MyApplication { }

// 等价于
@Configuration
@EnableAutoConfiguration
@ComponentScan
public class MyApplication { }

// 也等价于(因为 @SpringBootConfiguration 就是 @Configuration)
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
public class MyApplication { }

追问 2:@EnableAutoConfiguration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    // 排除不需要的自动配置
    Class<?>[] exclude() default {};

    // 排除不需要的自动配置类名
    String[] excludeName() default {};
}

追问 3:@ComponentScan 默认行为

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Scope("singleton")
public @interface ComponentScan {
    // 默认扫描当前包及其子包
    String[] basePackages() default {};

    // 默认扫描标注了这些注解的类
    Class<? extends Annotation>[] includeFilters() default {};

    // 默认排除标注了这些注解的类
    Class<? extends Annotation>[] excludeFilters() default {};
}
@ComponentScan 默认扫描规则:

假设应用入口类在 com.example.app 包下:

com.example.app.MyApplication  ← 入口类
com.example.app.service.UserService    ← 被扫描 ✓
com.example.app.controller.UserController  ← 被扫描 ✓
com.example.app.config.AppConfig      ← 被扫描 ✓

com.example.common.utils.StringUtils  ← 不被扫描 ✗
com.example.other.service.OrderService  ← 不被扫描 ✗
💡

这就是为什么推荐将应用入口类放在项目根包下的原因——确保所有子包都能被扫描到。

二、延伸问题 🟡

2.1 @SpringBootApplication 的可配置属性

@SpringBootApplication(
    scanBasePackages = {"com.example.app", "com.example.common"}, // 指定扫描包
    scanBasePackageClasses = {MyMarker.class}, // 指定扫描的类所在的包
    exclude = {DataSourceAutoConfiguration.class}, // 排除自动配置
    excludeName = {"org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration"}, // 排除自动配置(按名称)
    proxyBeanMethods = false // 是否使用代理 Bean(@Configuration 的 proxyBeanMethods)
)

2.2 最佳实践

// 方式1:使用 scanBasePackages 明确指定扫描包
@SpringBootApplication(scanBasePackages = {"com.example.app", "com.example.config"})
public class MyApplication { }

// 方式2:使用多个入口类分开扫描
@SpringBootApplication
@Import({App1Config.class, App2Config.class})
public class MainApplication { }

// 方式3:禁用自动配置(完全手动)
@SpringBootApplication(exclude = {AutoConfiguration.class})
public class MyApplication { }

三、面试总结

@SpringBootApplication 看似简单,实际上是三个核心注解的组合:

  • @Configuration:声明配置类
  • @EnableAutoConfiguration:启用自动配置
  • @ComponentScan:扫描组件

理解这三个注解的作用,就理解了 Spring Boot 应用的基本结构。