SpringBoot学习(五):自动配置的源码实现(一)@EnableAutoConfiguration详解

@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,即包含多个需要导入的类。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值