0. Рекомендация проекта с открытым исходным кодом
Pepper Metricsэто инструмент с открытым исходным кодом, разработанный мной и моими коллегами (GitHub.com/live-actioncool/pep…), который собирает текущую статистику производительности jedis/mybatis/httpservlet/dubbo/motan и предоставляет ее для основных данных, совместимых с базой данных временных рядов, таких как prometheus, и отображает тенденции через grafana. Его подключаемая архитектура также очень удобна для пользователей, позволяющих расширять и интегрировать другие компоненты с открытым исходным кодом.
Пожалуйста, поставьте звезду и пригласите всех стать разработчиками и отправить PR, чтобы вместе улучшить проект.
1 Обзор
Излишне говорить, что все знают, что Spring Boot очень удобен и быстр, позволяя разработчикам запускать и использовать проект с помощью всего нескольких строк кода плюс несколько строк конфигурации или даже нулевой конфигурации, Мы также можем часто использовать его в проектах.
@ConfigurationProperties привязывает бин к префиксу в конфигурации свойств, чтобы значение конфигурации было отделено от бина, определяющего конфигурацию, что удобно для управления.
Итак, каков механизм этого @ConfigurationProperties и как он реализован? Давайте сегодня поговорим на эту тему
2. Текст
2.1 Начните с EnableConfigurationProperties
Зачем говорить из EnableConfigurationProperties? Большое количество автоконфигураций в самом проекте Spring Boot использует аннотацию EnableConfigurationProperties для включения функции XXXProperties, например, spring-data-redis Эта RedisAutoConfiguration
@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class) //看这里
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
// ...
}
И RedisProperties аннотируется с помощью @ConfigurationProperties (prefix = "spring.redis"), так что элемент конфигурации с префиксом spring.redis и RedisProperties Этот класс сущностей связан.
2.2 Внутреннее разрешение реализации EnableConfigurationProperties
После разговора о причине, давайте поговорим о внутренней реализации, давайте сначала посмотрим
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(EnableConfigurationPropertiesImportSelector.class)
public @interface EnableConfigurationProperties {
Class<?>[] value() default {};
}
@Import(EnableConfigurationPropertiesImportSelector.class) указывает класс обработки EnableConfigurationPropertiesImportSelector этой аннотации, Просмотр исходного кода EnableConfigurationPropertiesImportSelector
class EnableConfigurationPropertiesImportSelector implements ImportSelector {
private static final String[] IMPORTS = { ConfigurationPropertiesBeanRegistrar.class.getName(),
ConfigurationPropertiesBindingPostProcessorRegistrar.class.getName() };
@Override
public String[] selectImports(AnnotationMetadata metadata) {
return IMPORTS;
}
// 省略部分其他方法
}
Давайте сначала посмотрим, что эта ключевая часть возвращает массив IMPORTS, который содержит два элемента: ConfigurationPropertiesBeanRegistrar.class, ConfigurationPropertiesBindingPostProcessorRegistrar.class.
По принципу интерфейса @Import и ImportSelector (по принципу можно сослаться на статью коллеги:С любовью к @Import и @EnableXXX), мы знаем, что Spring инициализирует два вышеупомянутых Registrar в контейнере spring, и оба Registrar реализуют интерфейс ImportBeanDefinitionRegistrar,
ImportBeanDefinitionRegistrar инициирует вызов при обработке Конфигурации (принцип можно посмотреть в статье:Найдите статью здесь), давайте углубимся в исходный код двух регистраторов соответственно:
- ConfigurationPropertiesBeanRegistrar
- ConfigurationPropertiesBindingPostProcessorRegistrar
2.2.1 ConfigurationPropertiesBindingPostProcessorRegistrar
смотри прямо на код
public class ConfigurationPropertiesBindingPostProcessorRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
if (!registry.containsBeanDefinition(ConfigurationPropertiesBindingPostProcessor.BEAN_NAME)) {
registerConfigurationPropertiesBindingPostProcessor(registry);
registerConfigurationBeanFactoryMetadata(registry);
}
}
private void registerConfigurationPropertiesBindingPostProcessor(BeanDefinitionRegistry registry) {
GenericBeanDefinition definition = new GenericBeanDefinition();
definition.setBeanClass(ConfigurationPropertiesBindingPostProcessor.class);
definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(ConfigurationPropertiesBindingPostProcessor.BEAN_NAME, definition);
}
private void registerConfigurationBeanFactoryMetadata(BeanDefinitionRegistry registry) {
GenericBeanDefinition definition = new GenericBeanDefinition();
definition.setBeanClass(ConfigurationBeanFactoryMetadata.class);
definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(ConfigurationBeanFactoryMetadata.BEAN_NAME, definition);
}
}
Вы можете видеть, что два bean-компонента зарегистрированы в контейнере spring.
- ConfigurationPropertiesBindingPostProcessor
- Он реализует следующий интерфейс:
BeanPostProcessor, PriorityOrdered, ApplicationContextAware, InitializingBean
- PriorityOrdered
Ordered.HIGHEST_PRECEDENCE + 1 гарантирует досрочное исполнение, а не первое - ApplicationContextAware
Получите ApplicationContext и установите его во внутреннюю переменную - InitializingBean
Метод afterPropertiesSet вызывается при создании bean-компонента, чтобы убедиться, что внутренняя переменная configurationPropertiesBinder инициализирована.Этот класс связывания является ключевым классом инструмента для связывания значений префикса и свойстваBean. - BeanPostProcessor Метод postProcessBeforeInitialization обрабатывает конкретную логику привязки следующим образом:
- PriorityOrdered
- Он реализует следующий интерфейс:
BeanPostProcessor, PriorityOrdered, ApplicationContextAware, InitializingBean
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
ConfigurationProperties annotation = getAnnotation(bean, beanName, ConfigurationProperties.class);
if (annotation != null) {
bind(bean, beanName, annotation);
}
return bean;
}
private void bind(Object bean, String beanName, ConfigurationProperties annotation) {
ResolvableType type = getBeanType(bean, beanName);
Validated validated = getAnnotation(bean, beanName, Validated.class);
Annotation[] annotations = (validated != null) ? new Annotation[] { annotation, validated }
: new Annotation[] { annotation };
Bindable<?> target = Bindable.of(type).withExistingValue(bean).withAnnotations(annotations);
try {
// 在这里完成了,关键的prefix到PropertyBean的值绑定部分,所以各种@ConfigurationProperties注解最终生效就靠这部分代码了
this.configurationPropertiesBinder.bind(target);
}
catch (Exception ex) {
throw new ConfigurationPropertiesBindException(beanName, bean, annotation, ex);
}
}
- ConfigurationBeanFactoryMetadata
Если некоторые bean-компоненты создаются с помощью FactoryBean, этот класс используется для сохранения различной исходной информации FactoryBean, которая используется для запроса метаданных в ConfigurationPropertiesBindingPostProcessor, который здесь не будет раскрываться.
2.2.2 ConfigurationPropertiesBeanRegistrar
На самом деле ConfigurationPropertiesBeanRegistrar — это статический внутренний класс EnableConfigurationPropertiesImportSelector, часть, которая была пропущена при вставке кода ранее, вышеприведенный код
public static class ConfigurationPropertiesBeanRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
getTypes(metadata).forEach((type) -> register(registry, (ConfigurableListableBeanFactory) registry, type));
}
private List<Class<?>> getTypes(AnnotationMetadata metadata) {
MultiValueMap<String, Object> attributes = metadata
.getAllAnnotationAttributes(EnableConfigurationProperties.class.getName(), false);
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());
}
private void register(BeanDefinitionRegistry registry, ConfigurableListableBeanFactory beanFactory,
Class<?> type) {
String name = getName(type);
if (!containsBeanDefinition(beanFactory, name)) {
registerBeanDefinition(registry, name, type);
}
}
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());
}
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) {...}
private boolean containsBeanDefinition(ConfigurableListableBeanFactory beanFactory, String name) {...}
}
Логическая интерпретация:
Основная логическая запись (registerBeanDefinitions)
1 -> getTypes(metadata) получает значения конфигурации, помеченные аннотацией EnableConfigurationProperties, упорядочивает их в List
3. Резюме
Общий процесс обработки @ConfigurationProperties был в основном описан в этой статье, и теперь он в общих чертах: EnableConfigurationProperties завершает знакомство с ConfigurationPropertiesBindingPostProcessorRegistrar и ConfigurationPropertiesBeanRegistrar. в:
- ConfigurationPropertiesBeanRegistrar завершает поиск классов с пометкой @ConfigurationProperties и собирает их в BeanDefinition для присоединения к реестру.
- ConfigurationPropertiesBindingPostProcessorRegistrar завершает ConfigurationPropertiesBindingPostProcessor и ConfigurationBeanFactoryMetadata
- ConfigurationPropertiesBindingPostProcessor завершает привязку всех bean-компонентов, отмеченных @ConfigurationProperties, к значению свойств префикса
- ConfigurationBeanFactoryMetadata используется только для предоставления некоторой информации метаданных, необходимой для вышеуказанной обработки.
4. Другие статьи автора
GitHub.com/live версия классная/нет…