Commit ce028240 authored by Madhura Bhave's avatar Madhura Bhave

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
parent ff1e5076
......@@ -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<BeanFactoryPostProcessor> postProcessors = context
.getBeanFactoryPostProcessors();
return postProcessors.stream().anyMatch((
postProcessor) -> postProcessor instanceof LazyInitializationBeanFactoryPostProcessor);
}
private void setClassLoaderIfPossible(ConfigurableApplicationContext child) {
if (child instanceof DefaultResourceLoader) {
((DefaultResourceLoader) child)
......
......@@ -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]]
......
/*
* 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;
}
}
......@@ -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;
}
}
}
......@@ -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<ConfigurableEnvironment> matchingPropertySource(
final Class<?> propertySourceClass, final String name) {
return new Condition<ConfigurableEnvironment>("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 {
......
/*
* 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<String> 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\"");
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment