6

系统学习SpringBoot

 1 year ago
source link: https://dcbupt.github.io/2022/10/09/blog_article/%E7%B3%BB%E7%BB%9F%E5%AD%A6%E4%B9%A0%E7%B3%BB%E5%88%97/%E7%B3%BB%E7%BB%9F%E5%AD%A6%E4%B9%A0SpringBoot/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

@EnableConfigurationProperties

当你开发一个中间件,或者一个通用组件的时候,通常需要给使用方提供一组可自定义的配置项,这些配置项往往在properties文件里定义,且带有特定前缀用于区分是这个组件的配置。那么如何能在应用启动后读到这些使用方自定义的配置项,影响组件的运行态呢?这时可以借助注解EnableConfigurationProperties

注解EnableConfigurationProperties通常加在你开发的中间件或通用组件的配置类上,注解的value表示需要注册到使用方bf的properties应用配置bean,该bean能把使用方的配置项注入进来

@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 的全局配置

@ConfigurationProperties(prefix = "rocketmq")
public class RocketMQProperties {
...
}

接下来从@EnableConfigurationProperties 注解开始,分析它是如何完成注册应用配置 bean 的

EnableConfigurationPropertiesRegistrar

@EnableConfigurationProperties 注解主要通过 Import 方式引入了一个 bd 注册器 EnableConfigurationPropertiesRegistrar,看下它的注册 bd 方法

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

static void registerInfrastructureBeans(BeanDefinitionRegistry registry) {
ConfigurationPropertiesBindingPostProcessor.register(registry);
BoundConfigurationProperties.register(registry);
}
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);
}
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);
}

}
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 注解

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}
this.getTypes(metadata).forEach(beanRegistrar::register);
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());
}
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

public void afterPropertiesSet() throws Exception {
this.registry = (BeanDefinitionRegistry) this.applicationContext.getAutowireCapableBeanFactory();
this.binder = ConfigurationPropertiesBinder.get(this.applicationContext);
}

绑定器在构造函数里,通过 ac 拿到应用的所有配置项。包括系统属性、环境变量、应用配置

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,完成应用配置的绑定

public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
this.bind(ConfigurationPropertiesBean.get(this.applicationContext, bean, beanName));
return bean;
}
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完成绑定

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 在构造函数里拿到应用的所有配置项

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK