From 28eb9aebcfee662d1e2516da041557d1f460c182 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 4 Jun 2024 22:50:42 +0200 Subject: [PATCH] Add BeanFactoryInitializer callback before preInstantiateSingletons Closes gh-32836 --- .../beans/factory/BeanFactoryInitializer.java | 41 +++++++++++++++++++ .../ApplicationContextInitializer.java | 4 +- .../support/AbstractApplicationContext.java | 8 ++++ .../DynamicPropertySourceBeanInitializer.java | 35 +++------------- 4 files changed, 56 insertions(+), 32 deletions(-) create mode 100644 spring-beans/src/main/java/org/springframework/beans/factory/BeanFactoryInitializer.java diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactoryInitializer.java b/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactoryInitializer.java new file mode 100644 index 000000000000..30c08739bbbd --- /dev/null +++ b/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactoryInitializer.java @@ -0,0 +1,41 @@ +/* + * Copyright 2002-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.beans.factory; + +/** + * Callback interface for initializing a Spring {@link ListableBeanFactory} + * prior to entering the singleton pre-instantiation phase. Can be used to + * trigger early initialization of specific beans before regular singletons. + * + *

Can be programmatically applied to a {@code ListableBeanFactory} instance. + * In an {@code ApplicationContext}, beans of type {@code BeanFactoryInitializer} + * will be autodetected and automatically applied to the underlying bean factory. + * + * @author Juergen Hoeller + * @since 6.2 + * @param the bean factory type + * @see org.springframework.beans.factory.config.ConfigurableListableBeanFactory#preInstantiateSingletons() + */ +public interface BeanFactoryInitializer { + + /** + * Initialize the given bean factory. + * @param beanFactory the bean factory to bootstrap + */ + void initialize(F beanFactory); + +} diff --git a/spring-context/src/main/java/org/springframework/context/ApplicationContextInitializer.java b/spring-context/src/main/java/org/springframework/context/ApplicationContextInitializer.java index 42e750fc2476..107793410ea1 100644 --- a/spring-context/src/main/java/org/springframework/context/ApplicationContextInitializer.java +++ b/spring-context/src/main/java/org/springframework/context/ApplicationContextInitializer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -44,7 +44,7 @@ public interface ApplicationContextInitializer getEnvironment().resolvePlaceholders(strVal)); } + // Call BeanFactoryInitializer beans early to allow for initializing specific other beans early. + String[] initializerNames = beanFactory.getBeanNamesForType(BeanFactoryInitializer.class, false, false); + for (String initializerName : initializerNames) { + beanFactory.getBean(initializerName, BeanFactoryInitializer.class).initialize(beanFactory); + } + // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early. String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false); for (String weaverAwareName : weaverAwareNames) { diff --git a/spring-test/src/main/java/org/springframework/test/context/support/DynamicPropertySourceBeanInitializer.java b/spring-test/src/main/java/org/springframework/test/context/support/DynamicPropertySourceBeanInitializer.java index ace5ea8ad8e0..d30ba11a4829 100644 --- a/spring-test/src/main/java/org/springframework/test/context/support/DynamicPropertySourceBeanInitializer.java +++ b/spring-test/src/main/java/org/springframework/test/context/support/DynamicPropertySourceBeanInitializer.java @@ -19,55 +19,30 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.BeanFactoryAware; -import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.BeanFactoryInitializer; import org.springframework.beans.factory.ListableBeanFactory; -import org.springframework.context.weaving.LoadTimeWeaverAware; -import org.springframework.instrument.classloading.LoadTimeWeaver; -import org.springframework.lang.Nullable; import org.springframework.test.context.DynamicPropertySource; /** * Internal component which eagerly initializes beans created by {@code @Bean} * factory methods annotated with {@link DynamicPropertySource @DynamicPropertySource}. * - *

This class implements {@link LoadTimeWeaverAware} since doing so is - * currently the only way to have a component eagerly initialized before the - * {@code ConfigurableListableBeanFactory.preInstantiateSingletons()} phase. - * * @author Sam Brannen * @since 6.2 */ -class DynamicPropertySourceBeanInitializer implements BeanFactoryAware, InitializingBean, LoadTimeWeaverAware { +class DynamicPropertySourceBeanInitializer implements BeanFactoryInitializer { private static final Log logger = LogFactory.getLog(DynamicPropertySourceBeanInitializer.class); - @Nullable - private BeanFactory beanFactory; - - - @Override - public void setBeanFactory(BeanFactory beanFactory) { - this.beanFactory = beanFactory; - } @Override - public void afterPropertiesSet() { - if (!(this.beanFactory instanceof ListableBeanFactory lbf)) { - throw new IllegalStateException("BeanFactory must be set and must be a ListableBeanFactory"); - } - for (String name : lbf.getBeanNamesForAnnotation(DynamicPropertySource.class)) { + public void initialize(ListableBeanFactory beanFactory) { + for (String name : beanFactory.getBeanNamesForAnnotation(DynamicPropertySource.class)) { if (logger.isDebugEnabled()) { logger.debug("Eagerly initializing @DynamicPropertySource bean '%s'".formatted(name)); } - this.beanFactory.getBean(name); + beanFactory.getBean(name); } } - @Override - public void setLoadTimeWeaver(LoadTimeWeaver loadTimeWeaver) { - // no-op - } - }