diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/EagerLoadingBeanDefinitionPredicate.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/EagerLoadingBeanDefinitionPredicate.java
new file mode 100644
index 000000000000..fd9c5706d874
--- /dev/null
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/EagerLoadingBeanDefinitionPredicate.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2012-2018 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.boot;
+
+import java.util.function.Predicate;
+
+/**
+ * This predicate can be implemented by downstream projects to customize the behavior of
+ * the {@link LazyInitializationBeanFactoryPostProcessor}.
+ *
+ *
+ * There are edge cases (such as in DSLs that dynamically create additional beans) in
+ * which it is not easy to explicitly exclude a class from the lazy-loading behavior.
+ * Adding an instance of this predicate to the application context can be used for these
+ * edge cases.
+ *
+ * Returning "true" from this predicate will exclude a class from the lazy-loading
+ * process.
+ *
+ *
+ * Example:
+ *
+ *
+ * {@code
+ *
+ * @Bean
+ * public static EagerLoadingBeanDefinitionPredicate eagerLoadingBeanDefinitionPredicate() {
+ * return IntegrationFlow.class::isAssignableFrom;
+ * }}
+ *
+ * WARNING: Beans of this type will be instantiated very early in the spring application
+ * life cycle.
+ *
+ * @author Tyler Van Gorder
+ * @since 2.2.0
+ */
+public interface EagerLoadingBeanDefinitionPredicate extends Predicate> {
+
+}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/LazyInitializationBeanFactoryPostProcessor.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/LazyInitializationBeanFactoryPostProcessor.java
index e66135986d17..64aeb9d41919 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/LazyInitializationBeanFactoryPostProcessor.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/LazyInitializationBeanFactoryPostProcessor.java
@@ -16,6 +16,10 @@
package org.springframework.boot;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
@@ -26,16 +30,42 @@
/**
* {@link BeanFactoryPostProcessor} to set the lazy attribute on bean definition.
*
+ *
+ * This processor will not touch a bean definition that has already had its "lazy" flag
+ * explicitly set to "false".
+ *
+ *
+ * There are edge cases in which it is not easy to explicitly set the "lazy" flag to
+ * "false" (such as in DSLs that dynamically create additional beans) and therefore this
+ * class uses a customizer strategy that allows downstream projects to contribute
+ * predicates which impact if a class is considered for lazy-loading.
+ *
+ *
+ * Because this is a BeanFactoryPostProcessor, this class does not use dependency
+ * injection to collect the customizers. The post processor actually makes two passes
+ * through the bean definitions; the first is used to find and instantiate any
+ * {@link org.springframework.boot.EagerLoadingBeanDefinitionPredicate} and the second
+ * pass is where bean definitions are marked as lazy.
+ *
* @author Andy Wilkinson
* @author Madhura Bhave
+ * @author Tyler Van Gorder
* @since 2.2.0
*/
public final class LazyInitializationBeanFactoryPostProcessor implements BeanFactoryPostProcessor, Ordered {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
- for (String name : beanFactory.getBeanDefinitionNames()) {
- BeanDefinition beanDefinition = beanFactory.getBeanDefinition(name);
+
+ List eagerPredicateList = getEagerLoadingPredicatesFromContext(
+ beanFactory);
+
+ for (String beanName : beanFactory.getBeanDefinitionNames()) {
+ BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
+ if (eagerPredicateList.stream()
+ .anyMatch((predicate) -> predicate.test(beanFactory.getType(beanName, false)))) {
+ continue;
+ }
if (beanDefinition instanceof AbstractBeanDefinition) {
Boolean lazyInit = ((AbstractBeanDefinition) beanDefinition).getLazyInit();
if (lazyInit != null && !lazyInit) {
@@ -46,6 +76,25 @@ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
}
}
+ /**
+ * This method extracts the list of
+ * {@link org.springframework.boot.EagerLoadingBeanDefinitionPredicate} beans from the
+ * bean factory. Because this method is called early in the factory life cycle, we
+ * take care not to force the eager initialization of factory beans.
+ * @param beanFactory bean factory passed into the post-processor.
+ * @return a list of {@link EagerLoadingBeanDefinitionPredicate} that can be used to
+ * customize the behavior of this processor.
+ */
+ private List getEagerLoadingPredicatesFromContext(
+ ConfigurableListableBeanFactory beanFactory) {
+
+ Map eagerPredicates = beanFactory
+ .getBeansOfType(EagerLoadingBeanDefinitionPredicate.class, false, false);
+
+ return new ArrayList<>(eagerPredicates.values());
+
+ }
+
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java
index 9575deb03d04..63d9b4087b7c 100644
--- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java
+++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java
@@ -1113,8 +1113,16 @@ void lazyInitializationShouldNotApplyToBeansThatAreExplicitlyNotLazy() {
.getBean(AtomicInteger.class)).hasValue(1);
}
+ @Test
+ void lazyInitializationShouldNotApplyToBeansThatMatchPredicate() {
+ assertThat(new SpringApplication(NotLazyInitializationPredicateConfig.class)
+ .run("--spring.main.web-application-type=none", "--spring.main.lazy-initialization=true")
+ .getBean(AtomicInteger.class)).hasValue(1);
+ }
+
private Condition matchingPropertySource(final Class> propertySourceClass,
final String name) {
+
return new Condition("has property source") {
@Override
@@ -1421,6 +1429,34 @@ static class NotLazyBean {
}
+ @Configuration(proxyBeanMethods = false)
+ static class NotLazyInitializationPredicateConfig {
+
+ @Bean
+ AtomicInteger counter() {
+ return new AtomicInteger(0);
+ }
+
+ @Bean
+ NotLazyBean notLazyBean(AtomicInteger counter) {
+ return new NotLazyBean(counter);
+ }
+
+ @Bean
+ static EagerLoadingBeanDefinitionPredicate eagerLoadingBeanDefinitionPredicate() {
+ return NotLazyBean.class::isAssignableFrom;
+ }
+
+ static class NotLazyBean {
+
+ NotLazyBean(AtomicInteger counter) {
+ counter.getAndIncrement();
+ }
+
+ }
+
+ }
+
static class ExitStatusException extends RuntimeException implements ExitCodeGenerator {
@Override