diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/server/ManagementContextAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/server/ManagementContextAutoConfiguration.java index c7fa4ad0537..3e28e606d07 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/server/ManagementContextAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/server/ManagementContextAutoConfiguration.java @@ -16,7 +16,11 @@ package org.springframework.boot.actuate.autoconfigure.web.server; +import java.util.List; + import org.springframework.beans.factory.SmartInitializingSingleton; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.boot.LazyInitializationBeanFactoryPostProcessor; import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties; import org.springframework.boot.actuate.autoconfigure.web.ManagementContextFactory; import org.springframework.boot.actuate.autoconfigure.web.ManagementContextType; @@ -33,6 +37,7 @@ import org.springframework.context.ApplicationListener; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Configuration; import org.springframework.context.event.ContextClosedEvent; +import org.springframework.context.support.AbstractApplicationContext; import org.springframework.core.Ordered; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; @@ -135,6 +140,10 @@ public class ManagementContextAutoConfiguration { .createManagementContext(this.applicationContext, EnableChildManagementContextConfiguration.class, PropertyPlaceholderAutoConfiguration.class); + if (isLazyInitialization()) { + managementContext.addBeanFactoryPostProcessor( + new LazyInitializationBeanFactoryPostProcessor()); + } managementContext.setServerNamespace("management"); managementContext.setId(this.applicationContext.getId() + ":management"); setClassLoaderIfPossible(managementContext); @@ -144,6 +153,14 @@ public class ManagementContextAutoConfiguration { } } + protected boolean isLazyInitialization() { + AbstractApplicationContext context = (AbstractApplicationContext) this.applicationContext; + List postProcessors = context + .getBeanFactoryPostProcessors(); + return postProcessors.stream().anyMatch(( + postProcessor) -> postProcessor instanceof LazyInitializationBeanFactoryPostProcessor); + } + private void setClassLoaderIfPossible(ConfigurableApplicationContext child) { if (child instanceof DefaultResourceLoader) { ((DefaultResourceLoader) child) diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc index fd58bc89b04..df50149a954 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc @@ -119,6 +119,10 @@ on `SpringApplicationBuilder` or the `setLazyInitialization` method on spring.main.lazy-initialization=true ---- +TIP: If you want to disable lazy initialization for certain beans while using lazy +initialization for the rest of the application, you can explicitly set their lazy attribute +to false using the `@Lazy(false)` annotation. + [[boot-features-banner]] 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 new file mode 100644 index 00000000000..5bea70c5c0d --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/LazyInitializationBeanFactoryPostProcessor.java @@ -0,0 +1,57 @@ +/* + * Copyright 2012-2019 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 org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.core.Ordered; + +/** + * {@link BeanFactoryPostProcessor} to set the lazy attribute on bean definition. + * + * @author Andy Wilkinson + * @author Madhura Bhave + * @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); + if (beanDefinition instanceof AbstractBeanDefinition) { + Boolean lazyInit = ((AbstractBeanDefinition) beanDefinition) + .getLazyInit(); + if (lazyInit != null && !lazyInit) { + continue; + } + } + beanDefinition.setLazyInit(true); + } + } + + @Override + public int getOrder() { + return Ordered.HIGHEST_PRECEDENCE; + } + +} diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java index cf5a0a34a98..61d0f1178e4 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java @@ -34,10 +34,8 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.BeanUtils; -import org.springframework.beans.BeansException; import org.springframework.beans.CachedIntrospectionResults; import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader; import org.springframework.beans.factory.support.BeanDefinitionRegistry; @@ -61,7 +59,6 @@ import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; import org.springframework.context.support.AbstractApplicationContext; import org.springframework.context.support.GenericApplicationContext; import org.springframework.core.GenericTypeResolver; -import org.springframework.core.Ordered; import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.support.ConfigurableConversionService; @@ -1345,22 +1342,4 @@ public class SpringApplication { return new LinkedHashSet<>(list); } - private static final class LazyInitializationBeanFactoryPostProcessor - implements BeanFactoryPostProcessor, Ordered { - - @Override - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) - throws BeansException { - for (String name : beanFactory.getBeanDefinitionNames()) { - beanFactory.getBeanDefinition(name).setLazyInit(true); - } - } - - @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 0bbc794bbfe..5f0089dbb3c 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 @@ -75,6 +75,7 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext import org.springframework.context.annotation.AnnotationConfigUtils; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; import org.springframework.context.event.ApplicationEventMulticaster; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.event.SimpleApplicationEventMulticaster; @@ -1186,6 +1187,14 @@ public class SpringApplicationTests { .getBean(AtomicInteger.class)).hasValue(0); } + @Test + public void lazyInitializationShouldNotApplyToBeansThatAreExplicitlyNotLazy() { + assertThat(new SpringApplication(NotLazyInitializationConfig.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") { @@ -1475,6 +1484,30 @@ public class SpringApplicationTests { } + @Configuration(proxyBeanMethods = false) + static class NotLazyInitializationConfig { + + @Bean + public AtomicInteger counter() { + return new AtomicInteger(0); + } + + @Bean + @Lazy(false) + public NotLazyBean NotLazyBean(AtomicInteger counter) { + return new NotLazyBean(counter); + } + + static class NotLazyBean { + + NotLazyBean(AtomicInteger counter) { + counter.getAndIncrement(); + } + + } + + } + static class ExitStatusException extends RuntimeException implements ExitCodeGenerator { diff --git a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/sample/actuator/ManagementPortWithLazyInitializationTests.java b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/sample/actuator/ManagementPortWithLazyInitializationTests.java new file mode 100644 index 00000000000..34076c1d1c8 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/sample/actuator/ManagementPortWithLazyInitializationTests.java @@ -0,0 +1,55 @@ +/* + * Copyright 2012-2019 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 sample.actuator; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.boot.actuate.autoconfigure.web.server.LocalManagementPort; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for separate management and main service ports when + * lazy-initialization is enabled. + * + * @author Madhura Bhave + */ +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = { + "management.server.port=0", "spring.main.lazy-initialization=true" }) +public class ManagementPortWithLazyInitializationTests { + + @LocalManagementPort + private int managementPort; + + @Test + public void testHealth() { + ResponseEntity entity = new TestRestTemplate() + .withBasicAuth("user", "password").getForEntity( + "http://localhost:" + this.managementPort + "/actuator/health", + String.class); + assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(entity.getBody()).contains("\"status\":\"UP\""); + } + +}