Spring Boot:@EnableAutoConfiguration和@Configuration的区别

时间:2025-03-22 08:27:06

SpringBoot提倡通过annotation来进行bean的配置,现在spring-boot里面常用的两种创建bean的方式有@EnableAutoConfiguration和@Configuration两种方式。

@Configuration方式

Spring Application在启动的时候,@ComponentScan注解会扫描包(路径可以指定,默认的情况下就是这个目录所在的包开始扫描),当扫描到@Configuration注解以后,就会初始化这个类下面所有加了@Bean的方法,并初始化这个bean。

@Configuration
@Slf4j
public class SecondBeanConfiguration {  
  @Bean  
  @ConditionalOnMissingBean  
  public SecondBean secondBean() { 
    ("log second bean");    
    return new SecondBean("secondBean", 2);  
  }}

@EnableAutoConfiguration方式

@EnableAutoConfiguration开启Spring Boot的自动配置功能。什么是自动配置功能呢?简单点说就是Spring Boot根据依赖中的jar包,自动选择实例化某些配置

@EnableAutoConfiguration注解通过读取文件里面的EnableAutoConfiguration指定的类,来初始化指定类下面的所有加了@Bean的方法,并初始化这个bean。

文件:

=\
,\

具体代码


@Slf4j
@Configuration
public class AutoFirstBeanConfiguration {  
  @Bean  
  @ConditionalOnMissingBean  
  public AutoFirstBean autoFirstBean() {
     ("log auto first bean");   
     return new AutoFirstBean("autoFirstBean", 1);  
  }}

在Spring Boot中,我们会使用@SpringBootApplication来开启Spring Boot程序。在之前的文章中我们讲到了@SpringBootApplication相当于@EnableAutoConfiguration,@ComponentScan,@Configuration三者的集合。

两种方式的差异

  • 初始化的时机,@Configuration初始化的方式总是在@EnableAutoConfiguration初始化方式之前

  • @Configuration初始化的顺序和扫描的过程相关,并不能进行有效的进行指定,不方便确定文件加载的顺序

  • @EnableAutoConfiguration可以通过@AutoConfigureAfter、@AutoConfigureBefore 和 @AutoConfigureOrder来指定类的加载顺序

  • @Configuration初始化会先初始化所有被扫到加了@Configuration文件的@PostConstruct注解然后再初始化这些文件里面的@Bean注解,但是@EnableAutoConfiguration是根据文件来进行初始化的,所以会初始化完一个文件的@PostConstruct注解然后再初始化这个文件的@Bean注解,然后再接着处理另外的文件。

所以需要提供bean给其他jar包进行使用的时候,最好使用@EnableAutoConfiguration方式(spring-boot-starters里面的都是通过这种方式来进行提供的,他的所有初始化的过程全部在spring-boot-autoconfigure项目中),因为能更好的控制类文件的加载顺序。有助于维护更佳复杂的项目。

另外需要注意一点的就是,如果@EnableAutoConfiguration提供的类名称在扫描的路径之中,spring-boot会把这些类作为configuration先进行初始化了,然后@AutoConfigureAfter@AutoConfigureBefore 和 @AutoConfigureOrder 这类指定顺序的注解都会失效

@EnableAutoConfiguration剖析

先看一下@EnableAutoConfiguration的定义:

@Target()
@Retention()
@Documented
@Inherited
@AutoConfigurationPackage
@Import()public @interface EnableAutoConfiguration {

    String ENABLED_OVERRIDE_PROPERTY = "";

    /**
     * Exclude specific auto-configuration classes such that they will never be applied.
     * @return the classes to exclude
     */
    Class<?>[] exclude() default {};

    /**
     * Exclude specific auto-configuration class names such that they will never be
     * applied.
     * @return the class names to exclude
     * @since 1.3.0
     */
    String[] excludeName() default {};

}

注意这一行:@Import() AutoConfigurationImportSelector实现了ImportSelector接口,并会在实例化时调用selectImports。下面是其方法:

public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        }
        AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
                .loadMetadata();
        AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
                annotationMetadata);
        return (());
}

这个方法中的getCandidateConfigurations会从类加载器中查找所有的META-INF/,并加载其中实现了@EnableAutoConfiguration的类。

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap<String, String> result = (classLoader);
        if (result != null) {
            return result;
        }

        try {
            Enumeration<URL> urls = (classLoader != null ?
                    (FACTORIES_RESOURCE_LOCATION) :
                    (FACTORIES_RESOURCE_LOCATION));
            result = new LinkedMultiValueMap<>();
            while (()) {
                URL url = ();
                UrlResource resource = new UrlResource(url);
                Properties properties = (resource);
                for (<?, ?> entry : ()) {
                    String factoryTypeName = ((String) ()).trim();
                    for (String factoryImplementationName : ((String) ())) {
                        (factoryTypeName, ());
                    }
                }
            }
            (classLoader, result);
            return result;
        }
        catch (IOException ex) {
            throw new IllegalArgumentException("Unable to load factories from location [" +
                    FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
}

我们再看一下spring-boot-autoconfigure-2.2.中的META-INF/。

里面的内容是key=value形式的,我们重点关注一下EnableAutoConfiguration:

# Auto Configure
=\
,\
,\
,\
,\
,\
,\
,\
,\
...

这里只列出了一部分内容,根据上面的代码, 所有的EnableAutoConfiguration的实现都会被自动加载。这就是自动加载的原理了。

我们仔细去看具体的实现:

@Configuration(proxyBeanMethods = false)
@AutoConfigureAfter()
@ConditionalOnProperty(prefix = "", value = "enabled", havingValue = "true",
        matchIfMissing = false)public class SpringApplicationAdminJmxAutoConfiguration {
  ...

参考资料

  • Spring使用@Async注解

  • Spring中异步注解@Async的使用、原理及使用时可能导致的问题