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
This commit is contained in:
parent
ff1e5076dc
commit
ce0282406f
|
|
@ -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]]
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
||||
|
|
|
|||
|
|
@ -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<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\"");
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue