Skip to content

Commit ce02824

Browse files
committed
Skip lazy init for beans that explicitly set lazy to false
This commit also adds tests to ensure that the child management context works when lazy initialization is enabled. Also, it adds a BeanFactoryPostProcessor to the child context so that the server is created and listening for requests but other beans in the child context are not created until requested. See gh-16184
1 parent ff1e507 commit ce02824

File tree

6 files changed

+166
-21
lines changed

6 files changed

+166
-21
lines changed

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/server/ManagementContextAutoConfiguration.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@
1616

1717
package org.springframework.boot.actuate.autoconfigure.web.server;
1818

19+
import java.util.List;
20+
1921
import org.springframework.beans.factory.SmartInitializingSingleton;
22+
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
23+
import org.springframework.boot.LazyInitializationBeanFactoryPostProcessor;
2024
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
2125
import org.springframework.boot.actuate.autoconfigure.web.ManagementContextFactory;
2226
import org.springframework.boot.actuate.autoconfigure.web.ManagementContextType;
@@ -33,6 +37,7 @@
3337
import org.springframework.context.ConfigurableApplicationContext;
3438
import org.springframework.context.annotation.Configuration;
3539
import org.springframework.context.event.ContextClosedEvent;
40+
import org.springframework.context.support.AbstractApplicationContext;
3641
import org.springframework.core.Ordered;
3742
import org.springframework.core.env.ConfigurableEnvironment;
3843
import org.springframework.core.env.Environment;
@@ -135,6 +140,10 @@ public void onApplicationEvent(WebServerInitializedEvent event) {
135140
.createManagementContext(this.applicationContext,
136141
EnableChildManagementContextConfiguration.class,
137142
PropertyPlaceholderAutoConfiguration.class);
143+
if (isLazyInitialization()) {
144+
managementContext.addBeanFactoryPostProcessor(
145+
new LazyInitializationBeanFactoryPostProcessor());
146+
}
138147
managementContext.setServerNamespace("management");
139148
managementContext.setId(this.applicationContext.getId() + ":management");
140149
setClassLoaderIfPossible(managementContext);
@@ -144,6 +153,14 @@ public void onApplicationEvent(WebServerInitializedEvent event) {
144153
}
145154
}
146155

156+
protected boolean isLazyInitialization() {
157+
AbstractApplicationContext context = (AbstractApplicationContext) this.applicationContext;
158+
List<BeanFactoryPostProcessor> postProcessors = context
159+
.getBeanFactoryPostProcessors();
160+
return postProcessors.stream().anyMatch((
161+
postProcessor) -> postProcessor instanceof LazyInitializationBeanFactoryPostProcessor);
162+
}
163+
147164
private void setClassLoaderIfPossible(ConfigurableApplicationContext child) {
148165
if (child instanceof DefaultResourceLoader) {
149166
((DefaultResourceLoader) child)

spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ on `SpringApplicationBuilder` or the `setLazyInitialization` method on
119119
spring.main.lazy-initialization=true
120120
----
121121

122+
TIP: If you want to disable lazy initialization for certain beans while using lazy
123+
initialization for the rest of the application, you can explicitly set their lazy attribute
124+
to false using the `@Lazy(false)` annotation.
125+
122126

123127

124128
[[boot-features-banner]]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright 2012-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot;
18+
19+
import org.springframework.beans.BeansException;
20+
import org.springframework.beans.factory.config.BeanDefinition;
21+
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
22+
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
23+
import org.springframework.beans.factory.support.AbstractBeanDefinition;
24+
import org.springframework.core.Ordered;
25+
26+
/**
27+
* {@link BeanFactoryPostProcessor} to set the lazy attribute on bean definition.
28+
*
29+
* @author Andy Wilkinson
30+
* @author Madhura Bhave
31+
* @since 2.2.0
32+
*/
33+
public final class LazyInitializationBeanFactoryPostProcessor
34+
implements BeanFactoryPostProcessor, Ordered {
35+
36+
@Override
37+
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
38+
throws BeansException {
39+
for (String name : beanFactory.getBeanDefinitionNames()) {
40+
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(name);
41+
if (beanDefinition instanceof AbstractBeanDefinition) {
42+
Boolean lazyInit = ((AbstractBeanDefinition) beanDefinition)
43+
.getLazyInit();
44+
if (lazyInit != null && !lazyInit) {
45+
continue;
46+
}
47+
}
48+
beanDefinition.setLazyInit(true);
49+
}
50+
}
51+
52+
@Override
53+
public int getOrder() {
54+
return Ordered.HIGHEST_PRECEDENCE;
55+
}
56+
57+
}

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,8 @@
3434
import org.apache.commons.logging.LogFactory;
3535

3636
import org.springframework.beans.BeanUtils;
37-
import org.springframework.beans.BeansException;
3837
import org.springframework.beans.CachedIntrospectionResults;
3938
import org.springframework.beans.factory.config.BeanDefinition;
40-
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
4139
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
4240
import org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader;
4341
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
@@ -61,7 +59,6 @@
6159
import org.springframework.context.support.AbstractApplicationContext;
6260
import org.springframework.context.support.GenericApplicationContext;
6361
import org.springframework.core.GenericTypeResolver;
64-
import org.springframework.core.Ordered;
6562
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
6663
import org.springframework.core.convert.ConversionService;
6764
import org.springframework.core.convert.support.ConfigurableConversionService;
@@ -1345,22 +1342,4 @@ private static <E> Set<E> asUnmodifiableOrderedSet(Collection<E> elements) {
13451342
return new LinkedHashSet<>(list);
13461343
}
13471344

1348-
private static final class LazyInitializationBeanFactoryPostProcessor
1349-
implements BeanFactoryPostProcessor, Ordered {
1350-
1351-
@Override
1352-
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
1353-
throws BeansException {
1354-
for (String name : beanFactory.getBeanDefinitionNames()) {
1355-
beanFactory.getBeanDefinition(name).setLazyInit(true);
1356-
}
1357-
}
1358-
1359-
@Override
1360-
public int getOrder() {
1361-
return Ordered.HIGHEST_PRECEDENCE;
1362-
}
1363-
1364-
}
1365-
13661345
}

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
import org.springframework.context.annotation.AnnotationConfigUtils;
7676
import org.springframework.context.annotation.Bean;
7777
import org.springframework.context.annotation.Configuration;
78+
import org.springframework.context.annotation.Lazy;
7879
import org.springframework.context.event.ApplicationEventMulticaster;
7980
import org.springframework.context.event.ContextRefreshedEvent;
8081
import org.springframework.context.event.SimpleApplicationEventMulticaster;
@@ -1186,6 +1187,14 @@ public void lazyInitializationCanBeEnabled() {
11861187
.getBean(AtomicInteger.class)).hasValue(0);
11871188
}
11881189

1190+
@Test
1191+
public void lazyInitializationShouldNotApplyToBeansThatAreExplicitlyNotLazy() {
1192+
assertThat(new SpringApplication(NotLazyInitializationConfig.class)
1193+
.run("--spring.main.web-application-type=none",
1194+
"--spring.main.lazy-initialization=true")
1195+
.getBean(AtomicInteger.class)).hasValue(1);
1196+
}
1197+
11891198
private Condition<ConfigurableEnvironment> matchingPropertySource(
11901199
final Class<?> propertySourceClass, final String name) {
11911200
return new Condition<ConfigurableEnvironment>("has property source") {
@@ -1475,6 +1484,30 @@ static class LazyBean {
14751484

14761485
}
14771486

1487+
@Configuration(proxyBeanMethods = false)
1488+
static class NotLazyInitializationConfig {
1489+
1490+
@Bean
1491+
public AtomicInteger counter() {
1492+
return new AtomicInteger(0);
1493+
}
1494+
1495+
@Bean
1496+
@Lazy(false)
1497+
public NotLazyBean NotLazyBean(AtomicInteger counter) {
1498+
return new NotLazyBean(counter);
1499+
}
1500+
1501+
static class NotLazyBean {
1502+
1503+
NotLazyBean(AtomicInteger counter) {
1504+
counter.getAndIncrement();
1505+
}
1506+
1507+
}
1508+
1509+
}
1510+
14781511
static class ExitStatusException extends RuntimeException
14791512
implements ExitCodeGenerator {
14801513

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright 2012-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package sample.actuator;
18+
19+
import org.junit.Test;
20+
import org.junit.runner.RunWith;
21+
22+
import org.springframework.boot.actuate.autoconfigure.web.server.LocalManagementPort;
23+
import org.springframework.boot.test.context.SpringBootTest;
24+
import org.springframework.boot.test.web.client.TestRestTemplate;
25+
import org.springframework.http.HttpStatus;
26+
import org.springframework.http.ResponseEntity;
27+
import org.springframework.test.context.junit4.SpringRunner;
28+
29+
import static org.assertj.core.api.Assertions.assertThat;
30+
31+
/**
32+
* Integration tests for separate management and main service ports when
33+
* lazy-initialization is enabled.
34+
*
35+
* @author Madhura Bhave
36+
*/
37+
@RunWith(SpringRunner.class)
38+
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = {
39+
"management.server.port=0", "spring.main.lazy-initialization=true" })
40+
public class ManagementPortWithLazyInitializationTests {
41+
42+
@LocalManagementPort
43+
private int managementPort;
44+
45+
@Test
46+
public void testHealth() {
47+
ResponseEntity<String> entity = new TestRestTemplate()
48+
.withBasicAuth("user", "password").getForEntity(
49+
"http://localhost:" + this.managementPort + "/actuator/health",
50+
String.class);
51+
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
52+
assertThat(entity.getBody()).contains("\"status\":\"UP\"");
53+
}
54+
55+
}

0 commit comments

Comments
 (0)