@EnableConfigurationProperties 当你开发一个中间件,或者一个通用组件的时候,通常需要给使用方提供一组可自定义的配置项,这些配置项往往在properties文件里定义,且带有特定前缀用于区分是这个组件的配置。那么如何能在应用启动后读到这些使用方自定义的配置项,影响组件的运行态呢?这时可以借助注解EnableConfigurationProperties
注解EnableConfigurationProperties通常加在你开发的中间件或通用组件的配置类上,注解的value表示需要注册到使用方bf的properties应用配置bean
,该bean能把使用方的配置项注入进来
1 2 3 4 5 6 7 8 9 @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Import({EnableConfigurationPropertiesRegistrar.class}) public @interface EnableConfigurationProperties { String VALIDATOR_BEAN_NAME = "configurationPropertiesValidator"; Class<?>[] value() default {}; }
properties应用配置bean
需要使用@ConfigurationProperties 注解指定在应用属性文件里的属性前缀。例如 RocketMQ 使用 RocketMQProperties 这个 bean,注入 applicaiton.properties 里对 RocketMQ 的全局配置
1 2 3 4 @ConfigurationProperties(prefix = "rocketmq") public class RocketMQProperties { ... }
接下来从@EnableConfigurationProperties 注解开始,分析它是如何完成注册应用配置 bean 的
EnableConfigurationPropertiesRegistrar @EnableConfigurationProperties 注解主要通过 Import 方式引入了一个 bd 注册器 EnableConfigurationPropertiesRegistrar,看下它的注册 bd 方法
1 2 3 4 5 6 public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { registerInfrastructureBeans(registry); registerMethodValidationExcludeFilter(registry); ConfigurationPropertiesBeanRegistrar beanRegistrar = new ConfigurationPropertiesBeanRegistrar(registry); this.getTypes(metadata).forEach(beanRegistrar: :register); }
1、registerInfrastructureBeans 方法会注册一些基础设施 bean,包括 bpp:ConfigurationPropertiesBindingPostProcessor
、应用配置绑定器工厂:ConfigurationPropertiesBinder.Factory
、应用配置绑定器:ConfigurationPropertiesBinder
、应用配置容器:BoundConfigurationProperties
1 2 3 4 static void registerInfrastructureBeans(BeanDefinitionRegistry registry) { ConfigurationPropertiesBindingPostProcessor.register(registry); BoundConfigurationProperties.register(registry); }
1 2 3 4 5 6 7 8 9 10 public static void register(BeanDefinitionRegistry registry) { Assert.notNull(registry, "Registry must not be null"); if (!registry.containsBeanDefinition(BEAN_NAME)) { BeanDefinition definition = BeanDefinitionBuilder.genericBeanDefinition(ConfigurationPropertiesBindingPostProcessor.class, ConfigurationPropertiesBindingPostProcessor: :new).getBeanDefinition(); definition.setRole(2); registry.registerBeanDefinition(BEAN_NAME, definition); } ConfigurationPropertiesBinder.register(registry); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 static void register(BeanDefinitionRegistry registry) { AbstractBeanDefinition definition; if (!registry.containsBeanDefinition("org.springframework.boot.context.internalConfigurationPropertiesBinderFactory")) { definition = BeanDefinitionBuilder.genericBeanDefinition(ConfigurationPropertiesBinder.Factory.class, ConfigurationPropertiesBinder.Factory: :new).getBeanDefinition(); definition.setRole(2); registry.registerBeanDefinition("org.springframework.boot.context.internalConfigurationPropertiesBinderFactory", definition); } if (!registry.containsBeanDefinition("org.springframework.boot.context.internalConfigurationPropertiesBinder")) { definition = BeanDefinitionBuilder.genericBeanDefinition(ConfigurationPropertiesBinder.class, () - >{ return ((ConfigurationPropertiesBinder.Factory)((BeanFactory) registry).getBean("org.springframework.boot.context.internalConfigurationPropertiesBinderFactory", ConfigurationPropertiesBinder.Factory.class)).create(); }).getBeanDefinition(); definition.setRole(2); registry.registerBeanDefinition("org.springframework.boot.context.internalConfigurationPropertiesBinder", definition); } }
1 2 3 4 5 6 7 8 9 static void register(BeanDefinitionRegistry registry) { Assert.notNull(registry, "Registry must not be null"); if (!registry.containsBeanDefinition(BEAN_NAME)) { BeanDefinition definition = BeanDefinitionBuilder.genericBeanDefinition(BoundConfigurationProperties.class, BoundConfigurationProperties: :new).getBeanDefinition(); definition.setRole(2); registry.registerBeanDefinition(BEAN_NAME, definition); } }
2、registerMethodValidationExcludeFilter 方法注册了一个过滤器,过滤条件是必须包含 ConfigurationProperties 注解
1 2 3 4 5 6 7 8 9 static void registerMethodValidationExcludeFilter(BeanDefinitionRegistry registry) { if (!registry.containsBeanDefinition(METHOD_VALIDATION_EXCLUDE_FILTER_BEAN_NAME)) { BeanDefinition definition = BeanDefinitionBuilder.genericBeanDefinition(MethodValidationExcludeFilter.class, () - >{ return MethodValidationExcludeFilter.byAnnotation(ConfigurationProperties.class); }).setRole(2).getBeanDefinition(); registry.registerBeanDefinition(METHOD_VALIDATION_EXCLUDE_FILTER_BEAN_NAME, definition); } }
3、从父注解@EnableConfigurationProperties 的 value 属性中取出所有应用配置类 class,注册到 bf
如果应用配置类 class 通过 ConfigurationProperties 注解指定了前缀,beanName 的格式为:{前缀}-{class.getName}
1 this.getTypes(metadata).forEach(beanRegistrar::register);
1 2 3 4 5 6 7 private Set < Class < ?>>getTypes(AnnotationMetadata metadata) { return (Set) metadata.getAnnotations().stream(EnableConfigurationProperties.class).flatMap((annotation) - >{ return Arrays.stream(annotation.getClassArray("value")); }).filter((type) - >{ return Void.TYPE != type; }).collect(Collectors.toSet()); }
1 2 3 4 5 6 7 8 9 10 11 12 void register(Class < ?>type, MergedAnnotation < ConfigurationProperties > annotation) { String name = this.getName(type, annotation); if (!this.containsBeanDefinition(name)) { this.registerBeanDefinition(name, type, annotation); } } private String getName(Class < ?>type, MergedAnnotation < ConfigurationProperties > annotation) { String prefix = annotation.isPresent() ? annotation.getString("prefix") : ""; return StringUtils.hasText(prefix) ? prefix + "-" + type.getName() : type.getName(); }
ConfigurationPropertiesBindingPostProcessor 应用配置绑定 bpp。该 bpp 在初始化阶段,通过 ac 加载并持有绑定器:ConfigurationPropertiesBinder
1 2 3 4 public void afterPropertiesSet() throws Exception { this.registry = (BeanDefinitionRegistry) this.applicationContext.getAutowireCapableBeanFactory(); this.binder = ConfigurationPropertiesBinder.get(this.applicationContext); }
绑定器在构造函数里,通过 ac 拿到应用的所有配置项。包括系统属性、环境变量、应用配置
1 2 3 4 5 6 ConfigurationPropertiesBinder(ApplicationContext applicationContext) { this.applicationContext = applicationContext; this.propertySources = (new PropertySourcesDeducer(applicationContext)).getPropertySources(); this.configurationPropertiesValidator = this.getConfigurationPropertiesValidator(applicationContext); this.jsr303Present = ConfigurationPropertiesJsr303Validator.isJsr303Present(applicationContext); }
ConfigurationPropertiesBindingPostProcessor 在 bean 前置初始化阶段,对使用@ConfigurationProperties 修饰的 bean,完成应用配置的绑定
1 2 3 4 public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { this.bind(ConfigurationPropertiesBean.get(this.applicationContext, bean, beanName)); return bean; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public static ConfigurationPropertiesBean get(ApplicationContext applicationContext, Object bean, String beanName) { Method factoryMethod = findFactoryMethod(applicationContext, beanName); return create(beanName, bean, bean.getClass(), factoryMethod); } private static ConfigurationPropertiesBean create(String name, Object instance, Class < ?>type, Method factory) { ConfigurationProperties annotation = (ConfigurationProperties) findAnnotation(instance, type, factory, ConfigurationProperties.class); if (annotation == null) { return null; } else { ... return new ConfigurationPropertiesBean(name, instance, annotation, bindTarget); } }
内部使用绑定器ConfigurationPropertiesBinder
完成绑定
1 2 3 4 5 private void bind(ConfigurationPropertiesBean bean) { ... this.binder.bind(bean); }
已完成绑定的应用配置,会缓存在 BoundConfigurationProperties bean
总结 通过注解@EnableConfigurationProperties
开启 sb 的应用配置绑定 bean 能力,在该注解的 value 属性上指定接收应用配置绑定的 bean 该注解引入一个 bd 注册器:EnableConfigurationPropertiesRegistrar
,该注册器会将 @EnableConfigurationProperties 指定的应用配置绑定 bean 注册到 bf,还会注册一些用于完成应用配置绑定的组件 bean,最重要的是两个 bean:
bpp:ConfigurationPropertiesBindingPostProcessor
。它在 bean 前置初始化阶段完成应用配置绑定注入
绑定器:ConfigurationPropertiesBinder
。ConfigurationPropertiesBindingPostProcessor 依赖该绑定器 bean 实现绑定注入。绑定器通过 ac 在构造函数里拿到应用的所有配置项