Spring Boot : EnableConfigurationPropertiesImportSelector

博客主要介绍了一个实现,其目的是将外部属性绑定到使用注解的属性配置上。具体绑定动作由注册到容器的组件完成,还提到该实现会被注解导入以确保工作组件就绪,同时给出了源代码版本及相关文章。

概述

EnableConfigurationPropertiesImportSelectorSpring Boot提供的一个ImportSelector实现,其目的是绑定外部属性到使用了注解@ConfigurationProperties的属性配置bean上。但具体的属性绑定的动作并不是由该ImportSelector来完成的,而是由它所指定的两个ImportBeanDefinitionRegistrar所注册到容器的bean来完成的 :

  • ConfigurationPropertiesBeanRegistrar

    ImportBeanDefinitionRegistrarEnableConfigurationPropertiesImportSelector定义的一个内部类,它用于获取配置类上注解 @EnableConfigurationProperties所指定要注册为bean的属性配置类,并将它们作为bean注册到容器。

  • ConfigurationPropertiesBindingPostProcessorRegistrar

    EnableConfigurationPropertiesImportSelector通过ConfigurationPropertiesBindingPostProcessorRegistrar注册一个ConfigurationPropertiesBindingPostProcessor到容器,而ConfigurationPropertiesBindingPostProcessor的任务则是绑定外部属性到使用了注解@ConfigurationProperties的属性配置bean上。

关于应用

EnableConfigurationPropertiesImportSelector会被注解@EnableConfigurationProperties导入,以确保属性绑定所需的ConfigurationPropertiesBeanRegistrarConfigurationPropertiesBindingPostProcessor两个工作组件提前就绪:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(EnableConfigurationPropertiesImportSelector.class)
public @interface EnableConfigurationProperties {

	Class<?>[] value() default {};

}

源代码

源代码版本 : ``

package org.springframework.boot.context.properties;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.Assert;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;


class EnableConfigurationPropertiesImportSelector implements ImportSelector {

	private static final String[] IMPORTS = {
			ConfigurationPropertiesBeanRegistrar.class.getName(),
			ConfigurationPropertiesBindingPostProcessorRegistrar.class.getName() };

	@Override
	public String[] selectImports(AnnotationMetadata metadata) {
		return IMPORTS;
	}

	/**
	 *  ImportBeanDefinitionRegistrar for configuration properties support.
	 * 内部类,一个 ImportBeanDefinitionRegistrar 实现,获取配置类上
     * 注解 @EnableConfigurationProperties 所
	 *  指定要注册为bean的属性配置类,并将它们作为bean注册到容器
	 */
	public static class ConfigurationPropertiesBeanRegistrar
			implements ImportBeanDefinitionRegistrar {

		// 获取注解元数据 metadata 所属类上注解 @EnableConfigurationProperties 所
		// 指定要注册为bean的属性配置类,并将它们作为bean注册到容器
		@Override
		public void registerBeanDefinitions(AnnotationMetadata metadata,
				BeanDefinitionRegistry registry) {
			getTypes(metadata).forEach((type) -> register(registry,
					(ConfigurableListableBeanFactory) registry, type));
		}

		// 获取注解元数据 metadata 所属类上注解 @EnableConfigurationProperties 的元数据,
		// 分析出其中要注册的目标 bean 的类,这里的目标 bean 通常是一些配置属性 bean ,也就是
		// 使用了注解 @ConfigurationProperties 的配置属性 bean 
		private List<Class<?>> getTypes(AnnotationMetadata metadata) {
			// 获取注解 @EnableConfigurationProperties 的所有属性,记录到
			// attributes,每个属性可能有多个值,所以使用数据结构 MultiValueMap
			MultiValueMap<String, Object> attributes = metadata
					.getAllAnnotationAttributes(
							EnableConfigurationProperties.class.getName(), false);
			// 从 @EnableConfigurationProperties 注解的 value 属性中分析出类,
			// 这些事将要注册为 bean 的属性bean的类				
			return collectClasses((attributes != null) ? attributes.get("value")
					: Collections.emptyList());
		}

		private List<Class<?>> collectClasses(List<?> values) {
			return values.stream().flatMap((value) -> Arrays.stream((Object[]) value))
					.map((o) -> (Class<?>) o).filter((type) -> void.class != type)
					.collect(Collectors.toList());
		}

       // 根据目标bean类型确定目标bean的名称,如果同名bean尚未注册,则注册该bean 
		private void register(BeanDefinitionRegistry registry,
				ConfigurableListableBeanFactory beanFactory, Class<?> type) {
			// 根据目标bean类型,和注解@ConfigurationProperties中指定的前缀信息
			// 构建目标bean的名称
			String name = getName(type);
			if (!containsBeanDefinition(beanFactory, name)) {
				// 如果名称为 name 的 bean 定义不存在于容器中,则注册之
				registerBeanDefinition(registry, name, type);
			}
		}

		// 根据目标bean类型上的注解 @ConfigurationProperties 元数据中配置属性项前缀信息
		// 和目标bean类型构建目标bean的名称:
		// 1. 如果注解 @ConfigurationProperties 被使用并且指定了前缀 , 返回 : 前缀-类名
		// 2. 否则返回 : 类名
		private String getName(Class<?> type) {
			ConfigurationProperties annotation = AnnotationUtils.findAnnotation(type,
					ConfigurationProperties.class);
			String prefix = (annotation != null) ? annotation.prefix() : "";
			return (StringUtils.hasText(prefix) ? prefix + "-" + type.getName()
					: type.getName());
		}

		// 如果容器中已经包含指定名称的bean则返回true,否则返回false
		private boolean containsBeanDefinition(
				ConfigurableListableBeanFactory beanFactory, String name) {
			if (beanFactory.containsBeanDefinition(name)) {
				return true;
			}
			BeanFactory parent = beanFactory.getParentBeanFactory();
			if (parent instanceof ConfigurableListableBeanFactory) {
				return containsBeanDefinition((ConfigurableListableBeanFactory) parent,
						name);
			}
			return false;
		}

		// 使用指定的名称name注册bean
		private void registerBeanDefinition(BeanDefinitionRegistry registry, String name,
				Class<?> type) {
			assertHasAnnotation(type);
			GenericBeanDefinition definition = new GenericBeanDefinition();
			definition.setBeanClass(type);
			registry.registerBeanDefinition(name, definition);
		}

		private void assertHasAnnotation(Class<?> type) {
			Assert.notNull(
					AnnotationUtils.findAnnotation(type, ConfigurationProperties.class),
					() -> "No " + ConfigurationProperties.class.getSimpleName()
							+ " annotation found on  '" + type.getName() + "'.");
		}

	}

}

相关文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值