Insight springboot boot spring - EnableAutoConfiguration

springboot boot spring 的方案除了前一篇文章提到的,通过 SpringApplicationRunListener 暴露spring 框架启动的阶段,为spring 容器的初始化各种事件的扩展提供方案。

另外一个boot spring 的方案就是 auto-configuration,通过个各种starter,提供各种EnableAutoConfiguration 接口的实现,将对应的特性注册到spring容器。

springboot auto-configuration 原理

首先,spring 框架支持以@Configuration 注解的形式配置Bean,以@Import 引入并注册Bean。springboot 自定义对应的ImportSelector,将各种starter 提供的各种@Configuration 配置类引入到spring 容器中。

然后,基于spring的Condition 机制,通过扩展@Conditional,提供更加丰富、具体的选择判断的功能,支持根据当前classpath或者spring 容器的情况判断是否注册Bean。最终只会有效合法的Bean 注册到spring 容器中。

接下来,针对上述描述的过程,从springboot 入手,逐步分析关键注解的作用。

insight springboot @EnableAutoConfiguration

// EnableAutoConfiguration 的作用就是引入自定义ImportSelector,识别和引入configuration
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}

借助SpringFactoriesLoader 提供的通用工厂模式机制,springboot 可以加载到classpath 下的configuration classes。

// @see AutoConfigurationImportSelector#selectImports
public String[] selectImports(AnnotationMetadata annotationMetadata) {
	// find auto configuration classes in META-INF/spring.factories
	List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
	configurations = removeDuplicates(configurations);
	configurations = sort(configurations, autoConfigurationMetadata);
	Set<String> exclusions = getExclusions(annotationMetadata, attributes);
	configurations.removeAll(exclusions);
	configurations = filter(configurations, autoConfigurationMetadata);
	fireAutoConfigurationImportEvents(configurations, exclusions);
	return configurations.toArray(new String[configurations.size()]);
}

insight spring @Conditional

相当于TypeFilter 增强版,通过自定义编程的方式进行判断和筛选bean definition 。

springboot 扩展的注解:@ConditionalOnBean、@ConditionalOnClass、@ConditionalOnProperty 等

/**
 * Determine if an item should be skipped based on @Conditional annotations.
 * 
 * @see ConditionEvaluator#shouldSkip
 */
public boolean shouldSkip(AnnotatedTypeMetadata metadata, ConfigurationPhase phase) {
	// 规则:无条件=默认符合
    if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
		return false;
	}
	// 解析@Conditional 并初始化conditions
	List<Condition> conditions = new ArrayList<Condition>();
	for (String[] conditionClasses : getConditionClasses(metadata)) {
		for (String conditionClass : conditionClasses) {
			Condition condition = getCondition(conditionClass);
			conditions.add(condition);
		}
	}
	// 根据conditions 判断是否matches
	for (Condition condition : conditions) {
		if (requiredPhase == null || requiredPhase == phase) {
			if (!condition.matches(this.context, metadata)) {
				return true;
			}
		}
	}

	return false;
}

insight spring Configuration annotations

自从spring 3.x开始,spring 支持以java 代码的形式配置容器。

关键的注解:@Configuration @Bean @Import @ComponentScans @PropertySources

@Configuration 类似spring XML configuration 的作用,加载配置文件、BeanDefinition等,因此应该在容器初始化初期进行。对应的处理类:ConfigurationClassPostProcessor,也就是BeanFactoryPostProcessor。

注意:上述的处理类实现了BeanDefinitionRegistryPostProcessor 接口,这个比标准的BeanFactoryPostProcessor 更早的调用和执行。

注册ConfigurationClassPostProcessor

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
	this.registry = registry;
	this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
	// 注册配置处理器,spring annotation特性核心支持
	AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}

ConfigurationClassPostProcessor 处理逻辑

类比xml configuration,@Configuration class 相当于某一个xml 文件。

处理逻辑分为两部分:

  1. 解析class内部的注解(类比xml文件的标签)
  2. 解析class 引入的class(类比xml 文件引入的其他xml文件配置)

处理逻辑的两个核心类:

  1. ConfigurationClassParser,解析Configuration class。
  2. ConfigurationClassBeanDefinitionReader,判断、注册BeanDefinitions。
// @see ConfigurationClassPostProcessor#processConfigBeanDefinitions
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
	
	// Parse each @Configuration class
	ConfigurationClassParser parser = new ConfigurationClassParser(
			this.metadataReaderFactory, this.problemReporter, this.environment,
			this.resourceLoader, this.componentScanBeanNameGenerator, registry);

	Set<BeanDefinitionHolder> candidates = new LinkedHashSet<BeanDefinitionHolder>(configCandidates);
	Set<ConfigurationClass> alreadyParsed = new HashSet<ConfigurationClass>(configCandidates.size());
	do {
        // 解析@Configuration class,对class 声明的@PropertySources、@ComponentScans、@ImportResource、@Bean、@Import 进行处理。
		parser.parse(candidates);
		parser.validate();

		// Read the model and create bean definitions based on its content
		if (this.reader == null) {
			this.reader = new ConfigurationClassBeanDefinitionReader(
					registry, this.sourceExtractor, this.resourceLoader, this.environment,
					this.importBeanNameGenerator, parser.getImportRegistry());
		}
        // 根据Conditional 判断是否注册到容器中
		this.reader.loadBeanDefinitions(configClasses);
		alreadyParsed.addAll(configClasses);
        // 判断是否有新引入的@Configuration class... 继续加载、解析、处理。
	}
	while (!candidates.isEmpty());
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值