Это 10-й день, когда я участвую в ноябрьской задаче, подробности о событии представление:Вызов последнего обновления 2021 г.
Эта статья связана с проблемами, возникшими в последних проектах, ошибки всегда появляются там, где вы думаете...
описание проблемы
Ниже приведен простой воспроизводимый фрагмент кода.Если вы можете сделать правильное суждение, не дочитав эту статью до конца, поздравляю, вы сэкономите время на чтении этой статьи.
1. Класс автоматической настройки:AutoTestConfiguration
@Configuration
@EnableConfigurationProperties(TestProperties.class)
@ConditionalOnProperty(prefix = "test", name = "enable")
public class AutoTestConfiguration {
@Bean
@ConditionalOnMissingBean
public TestBean testBean(TestProperties properties){
System.out.println("this is executed.....");
return new TestBean();
}
}
2. Класс конфигурацииTestProperties
@ConfigurationProperties(prefix = "test")
public class TestProperties {
private boolean enable = true;
public boolean isEnable() {
return enable;
}
public void setEnable(boolean enable) {
this.enable = enable;
}
}
Оба класса находятся вroot package
, можно гарантировать, что нормальнаяSpring
просканировал, значит проблемаTestBean
Будет ли он нормально создан? Конечно вывод тут никакой.
Некоторые одноклассники могут сказать, что выTestProperties
нет добавления@Configuration
аннотация,Spring
Не знаю, это действительно так? Очевидно нет.
В ходе расследования этой проблемы также встречаются другие проблемы, а также ранее не столкнулись; дажеSpring
Я много раз читал исходный код, но все еще есть некоторые закоулки и закоулки, которых вы, возможно, не ожидаете; ниже я решу эту проблему и постепенно ее раскрою.
Поведение аннотации @EnableConfigurationProperties
В предыдущих версияхTestProperties
был@Configuration
аннотированный
@Configuration // 可以被 spring 扫描
@ConfigurationProperties(prefix = "test")
public class TestProperties {
private boolean enable = true;
public boolean isEnable() {
return enable;
}
public void setEnable(boolean enable) {
this.enable = enable;
}
}
Общая идея заключается в том, что когдаTestProperties
После сканирования spring env будет иметьtest.enable=true
Наличие к-в, при исполненииAutoTestConfiguration
Когда класс автоконфигурации обновляется,@ConditionalOnProperty(prefix = "test", name = "enable")
вступит в силу, иTestBean
создается нормально.
Но это не так, ниже приведена проверка этой проблемы
Конфигурация действительна, AutoTestConfiguration не обновляется
Два момента:
- 1. Выполнение AutoTestConfiguration # TestBean выкроет журнал (используется для оценки того, обновляется ли autoTestConfiguration
- Прослушайте событие ApplicationReadyEvent, возьмите
test.enable
Значение (используется для оценки того, нормально ли загружается конфигурация терминала, то есть нормально ли обновляются TestProperties)
код показывает, как показано ниже:
@SpringBootApplication
public class Application implements ApplicationListener<ApplicationReadyEvent> {
@Autowired
private ApplicationContext applicationContext;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
System.out.println(this.applicationContext.getEnvironment().getProperty("test.enable") + "------");
}
}
Результат выполнения естьAutoTestConfiguration#testBean
не выполняется, аtest.enable
правда.
объяснил здесьTestProperties
Обновился, но не так@ConditionalOnProperty
Это работает, тогда вы можете в основном догадаться, что это на классе автоматической настройки@ConditionalOnProperty
и@EnableConfigurationProperties
порядок действий.
Перед проверкой порядка вопросов я пытаюсь добавить следующую конфигурацию в Applications.properties In, Re запустите проект:
test.enable=true
Здесь я получаю еще одну проблему конфликта бобов.
prefix-type
Сообщение об исключении выглядит следующим образом:
Parameter 0 of method testBean in com.glmapper.bridge.boot.config.AutoTestConfiguration required a single bean, but 2 were found:
- testProperties: defined in file [/Users/glmapper/Documents/project/exception-guides/target/classes/com/glmapper/bridge/boot/config/TestProperties.class]
- test-com.glmapper.bridge.boot.config.TestProperties: defined in null
появился здесьtest-com.glmapper.bridge.boot.config.TestProperties
Фасоль с таким названием. Я попытался проверить в коде, отображается ли данное имя компонента, но я его не нашел, есть только одна возможность, то есть это создается самой Spring.
Этот процесс очень продвинут на этапе весеннего обновления. При устранении этой проблемы все еще требовалось некоторое время. Наконец, местоположение проблемы было постоянно предварительно позиционировано для инициализации beansdefinitions, чтобы найти ее.
вот@EnableConfigurationProperties
Аннотация поведения, зависимостьEnableConfigurationPropertiesRegistrar
, Исходный код следующим образом:
class EnableConfigurationPropertiesRegistrar implements ImportBeanDefinitionRegistrar {
.getQualifiedAttributeName(EnableConfigurationPropertiesRegistrar.class, "methodValidationExcludeFilter");
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
registerInfrastructureBeans(registry);
registerMethodValidationExcludeFilter(registry);
ConfigurationPropertiesBeanRegistrar beanRegistrar = new ConfigurationPropertiesBeanRegistrar(registry);
// to register
getTypes(metadata).forEach(beanRegistrar::register);
}
Из кода легко увидеть, что EnableConfigurationPropertiesRegistrar зарегистрирует целевые метаданные как bean-компонент; продолжайте отладку и найдите bean-компонент, который генерирует имя формата префиксного типа.
Ниже приведен конкретный код getName
private String getName(Class<?> type, MergedAnnotation<ConfigurationProperties> annotation) {
// 拿 prefix
String prefix = annotation.isPresent() ? annotation.getString("prefix") : "";
// prefix + "-" + 类全限定名
return (StringUtils.hasText(prefix) ? prefix + "-" + type.getName() : type.getName());
}
Здесь мы сначала уточняем вопрос:
если вы используете
@EnableConfigurationProperties
Чтобы открыть класс конфигурации, не используйте аннотации, такие как @Configuration, которые могут быть распознаны сканированием Spring в классе конфигурации, чтобы избежать нескольких экземпляров одного и того же типа bean-компонента при последующем использовании.
@ConditionalOnProperty
Возвращаясь к проблеме, что конфигурация не действует, вот запись в официальном выпуске:GitHub.com/spring-pro — это…
Тем не менее, здесь все еще можно восстановить первопричину проблемы, проанализировав код, здесь в основном анализируется с двух сторон:
- Логика сопоставления значений @ConditionalOnProperty, вам необходимо указать, из какого PropertySource следует читать при сопоставлении значений.
- Логика для ошибки совпадения @ConditionalOnProperty и обновления bean-компонента
Логика соответствия @ConditionalOnProperty
Во-первых, это проблема сопоставления источника значения, когда @ConditionalOnProperty выполняет вычисление.Все исходные источники легко получить с помощью кода отладки, как показано на следующем рисунке:
Из DEBUG этот случай имеет четыре источника (в частности, как показано выше), фактически из исходного кода Source охватывает все Spring Env:
[ConfigurationPropertySourcesPropertySource {name='configurationProperties'},
StubPropertySource {name='servletConfigInitParams'},
StubPropertySource {name='servletContextInitParams'},
PropertiesPropertySource {name='systemProperties'}, OriginAwareSystemEnvironmentPropertySource {name='systemEnvironment'},
RandomValuePropertySource {name='random'},
OriginTrackedMapPropertySource {name='Config resource 'class path resource [application.properties]' via location 'optional:classpath:/''}]
Поэтому эта статья не является эффективным случаем, причина выше, причина выше этих PropertySource не тестирует. Технология, которая не будет обновлена, или он не обновлялся, или он обновлялся после классов автоматической настройки.
Логика пропуска @ConditionalOnProperty
Здесь мы в основном объясняем логическую связь между @ConditionalOnPropert и обновляемым bean-компонентом, которая специально реализована в классе ConditionEvaluator.
public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
// 1、没有 Conditional 注解,则扫描时不会跳过当前 bean
// 2、遍历 conditions 进行判断是否满足
}
Таким образом, для аннотации к классу автоконфигурации Conditional является предпосылкой того, разрешено ли обновлять текущий класс.Только если условие Conditional выполняется, текущий класс автоконфигурации будет добавлен в список bean-компонентов. Если условие не выполняется, bean-компонент будет пропущен напрямую и не будет помещен в BeandefinitonMap, поэтому последующего действия по обновлению не будет.
Время действия @ConditionalOnProperty предшествует созданию BeanDefiniton, и время его выполнения больше, чем@EnableConfigurationProperties
Эффект проявляется раньше, что также объясняет, почему test.enable=true в TestProperties,AutoTestConfiguration
И не обновляет почему.
Суммировать
В этой статье на простом примере прослеживается проблема, заключающаяся в том, что компонент не обновляется из-за сбоя конфигурации SpringBoot, обнаруженного в проекте.
- Примечания, связанные с условиями. Для класса автоматической конфигурации время более раннее, чтобы определить, разрешено ли обновление текущего класса автоматической конфигурации.
- Класс @EnableConfigurationProperties enable регистрирует bean-компонент по умолчанию, а формат имени bean-компонента является префиксным.