@EnableAutoConfiguration注解
-
@EnableAutoConfiguration为@SpringBootApplication注解的其中一个注解,作用是激活SpringBoot的自动配置功能。注解的定义如下:可以看到包含@AutoConfigurationPackage注解,通过@Import注解导入AutoConfigurationImportSelector。其中@AutoConfigurationPackage自身会导入AutoConfigurationPackages.Registrar,故@EnableAutoConfiguration注解导入了两个类AutoConfigurationImportSelector和AutoConfigurationPackages.Registrar。
-
其中自动配置主要是由AutoConfigurationImportSelector来完成的。AutoConfigurationImportSelector是DeferredImportSelector接口的实现类,在selectImports方法中,调用SpringFactoriesLoader.loadFactoryNames从META-INF/spring.factories文件中获取EnableAutoConfiguration对应的类列表,其中该类列表为需要自动配置的类的全限定名的列表,如org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; /** * 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 {}; } @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Import(AutoConfigurationPackages.Registrar.class) public @interface AutoConfigurationPackage { }
-
所以Spring容器在处理(使用ConfigurationClassPostProcessor,BeanFactoryPostProcessor接口的实现类)包含这个@EnableAutoConfiguration注解的类时,会处理内部的@Import注解来完成以上两个类的处理。
AutoConfigurationImportSelector
-
具体实现通过SpringFactoriesLoader.loadFactoryNames来从META-INF/spring.factories文件中,获取使用@Configuration注解的类全限定名列表。Spring容器会加载每个全限定名对应的配置类,并基于条件化配置,将该每个配置类内部使用@Bean注解的方法对应的Bean,可选地注册到Spring容器。类定义如下:实现了DeferredImportSelector接口,DeferredImportSelector继承于ImportSelector接口。
/** * {@link DeferredImportSelector} to handle {@link EnableAutoConfiguration * auto-configuration}. This class can also be subclassed if a custom variant of * {@link EnableAutoConfiguration @EnableAutoConfiguration} is needed. * * @since 1.3.0 * @see EnableAutoConfiguration */ public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered { ... }
ImportSelector
-
接口定义如下:
/** * Interface to be implemented by types that determine which @{@link Configuration} * class(es) should be imported based on a given selection criteria, usually one or more * annotation attributes. * ... * @since 3.1 * @see DeferredImportSelector * @see Import * @see ImportBeanDefinitionRegistrar * @see Configuration */ public interface ImportSelector { /** * Select and return the names of which class(es) should be imported based on * the {@link AnnotationMetadata} of the importing @{@link Configuration} class. */ String[] selectImports(AnnotationMetadata importingClassMetadata); }
-
ImportSelector接口与@Import功能差不多,主要用来导入使用@Configuration注解的类,不过ImportSelector可以更方便编程方式导入多个类,并支持条件化决定是否需要导入。接口定义如下:
public interface ImportSelector { /** * Select and return the names of which class(es) should be imported based on * the {@link AnnotationMetadata} of the importing @{@link Configuration} class. */ String[] selectImports(AnnotationMetadata importingClassMetadata); }
DeferredImportSelector:延迟版ImportSelector
-
DeferredImportSelector接口与ImportSelector接口的主要区别是:该接口实现类定义的需要导入Spring容器的类是延迟导入的。即会在所有其他@Configuration注解的类的BeanDefinition都被ConfigurationClassParser处理完之后,该接口实现类定义的类列表的导入操作,才被ConfigurationClassParser执行。ConfigurationClassParser的parse方法的源码实现如下:
public void parse(Set<BeanDefinitionHolder> configCandidates) { // 处理其他使用@Configuration注解的类 for (BeanDefinitionHolder holder : configCandidates) { BeanDefinition bd = holder.getBeanDefinition(); try { if (bd instanceof AnnotatedBeanDefinition) { parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName()); } else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) { parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName()); } else { parse(bd.getBeanClassName(), holder.getBeanName()); } } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex); } } // 延迟执行deferredImportSelector定义的类的处理 this.deferredImportSelectorHandler.process(); }
-
DeferredImportSelector接口,通常用来处理条件化注解@Conditional系列的类的导入,即由于应用定义的所有@Configuration注解的类都已经被处理完了,故可以检查该接口定义的需要导入的类是否已经在应用中定义过了,定义过则不导入,从而实现如@ConditionalOnMissingBean,@ConditionalOnMissingClass等条件化注解的语义。
-
DeferredImportSelector接口定义如下:
/** * A variation of {@link ImportSelector} that runs after all {@code @Configuration} beans * have been processed. This type of selector can be particularly useful when the selected * imports are {@code @Conditional}. */ public interface DeferredImportSelector extends ImportSelector { /** * Return a specific import group or {@code null} if no grouping is required. * @return the import group class or {@code null} */ @Nullable default Class<? extends Group> getImportGroup() { return null; } // 同一类型的importSelector(可以是一个importSelector实例或者属于一个importSelector类型的多个importSelector实例) // 所导入的多个importedClass为属于一个Group /** * Interface used to group results from different import selectors. */ interface Group { /** * Process the {@link AnnotationMetadata} of the importing @{@link Configuration} * class using the specified {@link DeferredImportSelector}. */ void process(AnnotationMetadata metadata, DeferredImportSelector selector); // // 从META-INF/spring.factories文件的EnableAutoConfigure注解 // 对应的需要自动加载的类全限定名列表 // 该列表都是String类型 /** * Return the {@link Entry entries} of which class(es) should be imported for this * group. */ Iterable<Entry> selectImports(); // 一个Entry代表一个需要导入的类 /** * An entry that holds the {@link AnnotationMetadata} of the importing * {@link Configuration} class and the class name to import. */ class Entry { private final AnnotationMetadata metadata; private final String importClassName; ... } } }
-
Group接口:包含了一个Group内部接口,Group接口又定义了一个内部类Entry,其中Group接口为同一类型的importSelector实例集合,所导入的多个importedClass为属于一个Group,即可以是一个importSelector实例或者属于一个importSelector类型的多个importSelector实例。DeferredImportSelectorGrouping为Group接口的一个实现类,所包含的importSelector的类型都是DeferredImportSelector。
- Group.Entry:一个Entry代表一个需要导入的类,包含类全限定名importClassName,类注解信息metadata两个属性。每个Group可以包含多个Entry,即包含多个需要导入的类。