@SpringBootApplication 注解(Spring Boot 自动配置)详解

一、引言

@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 注解内部组合了三个核心注解:

  1. @SpringBootConfiguration:标识配置类
  2. @EnableAutoConfiguration:启用自动配置
  3. @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 注解

作用:允许「被标注的注解」被子类继承(仅作用于类级注解)

注意限制

  1. 仅对 @Target(ElementType.TYPE) 的注解有效(方法/字段的注解不继承)
  2. 子类仅继承"父类的类级注解",不继承父接口的注解

源码

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 扫描指定的包路径

  • 在没有明确指定的前提下,默认是当前启动类所在的包路径,以及该包的所有子包
  • 可以通过 basePackagesbasePackageClasses 指定扫描路径

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 提供的「空实现基础类」,本身不包含固定排除规则,核心价值是「预留扩展接口」。

开发者可通过两种方式扩展其过滤逻辑:

  1. 手动实现 TypeExcludeFilter 子类,重写 match() 方法定义排除规则
  2. 通过 SpringApplication.addTypeExcludeFilter(TypeExcludeFilter filter) 动态添加排除规则
  3. 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 的自动配置类(如 DataSourceAutoConfigurationWebMvcAutoConfiguration),其加载方式不是 @ComponentScan 扫描,而是通过:

  1. @EnableAutoConfiguration 注解触发
  2. 读取 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件(Spring Boot 2.7+ 版本)
  3. 按需加载对应的自动配置类,注册为 Bean

这些自动配置类本身可能带有 @Configuration 注解(属于 @Component 的派生注解),如果不排除,会被 @ComponentScan 扫描到并「重复注册」------导致 Bean 冲突、配置覆盖等问题。

5.6.3 工作原理

AutoConfigurationExcludeFilter 会排除哪些类?

该 Filter 的 match() 方法会判断类是否满足以下条件,满足则排除(返回 true):

  1. 类上带有「自动配置相关注解」:@AutoConfigureBefore@AutoConfigureAfter@AutoConfigureBetween@AutoConfigureOrder@AutoConfigureImport
  2. 类属于 Spring Boot 自动配置类的「默认路径」(org.springframework.boot.autoconfigure 及其子包)
  3. 类被包含在「自动配置类清单」中(即 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.factoriesorg.springframework.boot.autoconfigure.EnableAutoConfiguration= 对应的类名列表。

6.5 自动配置的核心流程

6.5.1 完整流程

  1. 触发入口 :用户应用标注 @SpringBootApplication,其内部包含 @EnableAutoConfiguration 注解

  2. 导入选择器@EnableAutoConfiguration 注解通过 @Import(AutoConfigurationImportSelector.class),将该选择器注册到 Spring 容器

  3. 工具加载候选列表 :Spring 容器解析配置类时,调用 AutoConfigurationImportSelector.selectImports() 方法,该方法内部直接调用 SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, classLoader),读取所有 META-INF/spring.factories 中声明的自动配置类名(如 RedisAutoConfigurationDataSourceAutoConfiguration 等)

  4. 筛选与排序AutoConfigurationImportSelector 对候选列表做 3 件事:

    • 排除 :根据用户配置的 spring.autoconfigure.exclude@EnableAutoConfiguration(exclude) 剔除指定类
    • 条件过滤 :通过 AutoConfigurationMetadata 检查每个自动配置类的 @Conditional 注解(如 @ConditionalOnClass 检查依赖是否存在、@ConditionalOnMissingBean 检查用户是否自定义了 Bean),剔除不满足条件的类
    • 排序 :按 @AutoConfigureOrder 注解或类名排序,确保依赖类先加载(如 DataSourceAutoConfiguration 先于 JdbcTemplateAutoConfiguration
  5. 导入容器AutoConfigurationImportSelector 将筛选后的最终列表返回给 Spring 容器,Spring 会将这些类作为 @Configuration 类解析,注册其内部定义的 Bean(如 RedisTemplateDataSource 等)

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 工作流程

  1. 获取主程序包路径 :Spring 启动时,Registrar 会通过 AnnotationMetadata(注解元数据),获取到标注了 @AutoConfigurationPackage 的类(也就是你的主程序类,如 DemoApplication)的包名(比如 com.example.demo)。

  2. 注册包路径为 Spring BeanRegistrar 会将这个包名注册成一个特殊的 Spring Bean(类型为 BasePackagesBeanDefinition),本质是把包路径"记录"到 Spring 容器中。

  3. 自动配置类读取包路径 :后续的自动配置类(如 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. 应用启动完成
相关推荐
后端小张1 小时前
【JAVA 进阶】SpringBoot 事务深度解析:从理论到实践的完整指南
java·开发语言·spring boot·后端·spring·spring cloud·事务
间彧1 小时前
Docker Compose 数据卷挂载详解与项目实战
后端
合作小小程序员小小店1 小时前
web网页开发,在线%宠物销售%系统,基于Idea,html,css,jQuery,java,ssh,mysql。
java·前端·数据库·mysql·jdk·intellij-idea·宠物
合作小小程序员小小店1 小时前
web网页开发,在线%档案管理%系统,基于Idea,html,css,jQuery,java,ssh,mysql。
java·前端·mysql·jdk·html·ssh·intellij-idea
故渊ZY1 小时前
深入解析JVM:核心架构与调优实战
java·jvm·架构
ChinaRainbowSea1 小时前
13. Spring AI 的观测性
java·人工智能·后端·spring·flask·ai编程
-大头.1 小时前
SpringBoot 全面深度解析:从原理到实践,从入门到专家
java·spring boot·后端
Z_Easen1 小时前
Spring AI:Reactor 异步执行中的线程上下文传递实践
java·spring ai
合作小小程序员小小店1 小时前
web网页开发,在线%物流配送管理%系统,基于Idea,html,css,jQuery,java,ssh,mysql。
java·前端·css·数据库·jdk·html·intellij-idea