Provide a configuration option to enable lazy initialization
Closes gh-15870
This commit is contained in:
parent
6519e85b5a
commit
3a4a431eaa
|
|
@ -125,6 +125,7 @@ content into your application. Rather, pick only the properties that you need.
|
|||
# APPLICATION SETTINGS ({sc-spring-boot}/SpringApplication.{sc-ext}[SpringApplication])
|
||||
spring.main.allow-bean-definition-overriding=false # Whether bean definition overriding, by registering a definition with the same name as an existing definition, is allowed.
|
||||
spring.main.banner-mode=console # Mode used to display the banner when the application runs.
|
||||
spring.main.lazy-initialization=false # Whether initialization should be performed lazily.
|
||||
spring.main.sources= # Sources (class names, package names, or XML resource locations) to include in the ApplicationContext.
|
||||
spring.main.web-application-type= # Flag to explicitly request a specific type of web application. If not set, auto-detected based on the classpath.
|
||||
|
||||
|
|
|
|||
|
|
@ -91,6 +91,36 @@ the `debug` property as follows:
|
|||
|
||||
|
||||
|
||||
[[boot-features-lazy-initialization]]
|
||||
=== Lazy Initialization
|
||||
`SpringApplication` allows an application to be initialized lazily. When lazy
|
||||
initialization is enabled, beans are created as they are needed rather than during
|
||||
application startup. As a result, enabling lazy initialization can reduce the time that
|
||||
it takes your application to start. In a web application, enabling lazy initialization
|
||||
will result in many web-related beans not being initialized until an HTTP request is
|
||||
received.
|
||||
|
||||
A downside of lazy initialization is that it can delay the discovery of a problem with
|
||||
the application. If a misconfigured bean is initialized lazily, a failure will no longer
|
||||
occur during startup and the problem will only become apparent when the bean is
|
||||
initialized. Care must also be taken to ensure that the JVM has sufficient memory to
|
||||
accommodate all of the application's beans and not just those that are initialized during
|
||||
startup. For these reasons, lazy initialization is not enabled by default and it is
|
||||
recommended that fine-tuning of the JVM's heap size is done before enabling lazy
|
||||
initialization.
|
||||
|
||||
Lazy initialization can be enabled programatically using the `lazyInitialization` method
|
||||
on `SpringApplicationBuilder` or the `setLazyInitialization` method on
|
||||
`SpringApplication`. Alternatively, it can be enabled using the
|
||||
`spring.main.lazy-initialization` property as shown in the following example:
|
||||
|
||||
[source,properties,indent=0]
|
||||
----
|
||||
spring.main.lazy-initialization=true
|
||||
----
|
||||
|
||||
|
||||
|
||||
[[boot-features-banner]]
|
||||
=== Customizing the Banner
|
||||
The banner that is printed on start up can be changed by adding a `banner.txt` file to
|
||||
|
|
|
|||
|
|
@ -34,7 +34,10 @@ 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;
|
||||
|
|
@ -58,6 +61,7 @@ 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;
|
||||
|
|
@ -235,6 +239,8 @@ public class SpringApplication {
|
|||
|
||||
private boolean isCustomEnvironment = false;
|
||||
|
||||
private boolean lazyInitialization = false;
|
||||
|
||||
/**
|
||||
* Create a new {@link SpringApplication} instance. The application context will load
|
||||
* beans from the specified primary sources (see {@link SpringApplication class-level}
|
||||
|
|
@ -386,6 +392,10 @@ public class SpringApplication {
|
|||
((DefaultListableBeanFactory) beanFactory)
|
||||
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
|
||||
}
|
||||
if (this.lazyInitialization) {
|
||||
context.addBeanFactoryPostProcessor(
|
||||
new LazyInitializationBeanFactoryPostProcessor());
|
||||
}
|
||||
// Load the sources
|
||||
Set<Object> sources = getAllSources();
|
||||
Assert.notEmpty(sources, "Sources must not be empty");
|
||||
|
|
@ -979,6 +989,16 @@ public class SpringApplication {
|
|||
this.allowBeanDefinitionOverriding = allowBeanDefinitionOverriding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets if beans should be initialized lazily. Defaults to {@code false}.
|
||||
* @param lazyInitialization if initialization should be lazy
|
||||
* @since 2.2
|
||||
* @see BeanDefinition#setLazyInit(boolean)
|
||||
*/
|
||||
public void setLazyInitialization(boolean lazyInitialization) {
|
||||
this.lazyInitialization = lazyInitialization;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets if the application is headless and should not instantiate AWT. Defaults to
|
||||
* {@code true} to prevent java icons appearing.
|
||||
|
|
@ -1325,4 +1345,22 @@ 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -395,6 +395,17 @@ public class SpringApplicationBuilder {
|
|||
return properties(getMapFromKeyValuePairs(defaultProperties));
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag to control whether the application should be initialized lazily.
|
||||
* @param lazyInitialization the flag to set. Defaults to false.
|
||||
* @return the current builder
|
||||
* @since 2.2
|
||||
*/
|
||||
public SpringApplicationBuilder lazyInitialization(boolean lazyInitialization) {
|
||||
this.application.setLazyInitialization(lazyInitialization);
|
||||
return this;
|
||||
}
|
||||
|
||||
private Map<String, Object> getMapFromKeyValuePairs(String[] properties) {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
for (String property : properties) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -87,18 +87,15 @@ public class ReactiveWebServerApplicationContext
|
|||
private void createWebServer() {
|
||||
ServerManager serverManager = this.serverManager;
|
||||
if (serverManager == null) {
|
||||
this.serverManager = ServerManager.get(getWebServerFactory());
|
||||
String webServerFactoryBeanName = getWebServerFactoryBeanName();
|
||||
boolean lazyInit = getBeanFactory()
|
||||
.getBeanDefinition(webServerFactoryBeanName).isLazyInit();
|
||||
this.serverManager = ServerManager.get(getWebServerFactory(), lazyInit);
|
||||
}
|
||||
initPropertySources();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link ReactiveWebServerFactory} that should be used to create the
|
||||
* reactive web server. By default this method searches for a suitable bean in the
|
||||
* context itself.
|
||||
* @return a {@link ReactiveWebServerFactory} (never {@code null})
|
||||
*/
|
||||
protected ReactiveWebServerFactory getWebServerFactory() {
|
||||
protected String getWebServerFactoryBeanName() {
|
||||
// Use bean names so that we don't consider the hierarchy
|
||||
String[] beanNames = getBeanFactory()
|
||||
.getBeanNamesForType(ReactiveWebServerFactory.class);
|
||||
|
|
@ -113,7 +110,24 @@ public class ReactiveWebServerApplicationContext
|
|||
+ "ReactiveWebServerFactory beans : "
|
||||
+ StringUtils.arrayToCommaDelimitedString(beanNames));
|
||||
}
|
||||
return getBeanFactory().getBean(beanNames[0], ReactiveWebServerFactory.class);
|
||||
return beanNames[0];
|
||||
}
|
||||
|
||||
protected ReactiveWebServerFactory getWebServerFactory(String factoryBeanName) {
|
||||
return getBeanFactory().getBean(factoryBeanName, ReactiveWebServerFactory.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link ReactiveWebServerFactory} that should be used to create the
|
||||
* reactive web server. By default this method searches for a suitable bean in the
|
||||
* context itself.
|
||||
* @return a {@link ReactiveWebServerFactory} (never {@code null})
|
||||
* @deprecated since 2.2 in favor of {@link #getWebServerFactoryBeanName()} and
|
||||
* {@link #getWebServerFactory(String)}
|
||||
*/
|
||||
@Deprecated
|
||||
protected ReactiveWebServerFactory getWebServerFactory() {
|
||||
return getWebServerFactory(getWebServerFactoryBeanName());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -187,6 +201,24 @@ public class ReactiveWebServerApplicationContext
|
|||
this.serverNamespace = serverNamespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link HttpHandler} that initializes its delegate on first request.
|
||||
*/
|
||||
private static final class LazyHttpHandler implements HttpHandler {
|
||||
|
||||
private final Mono<HttpHandler> delegate;
|
||||
|
||||
private LazyHttpHandler(Mono<HttpHandler> delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
|
||||
return this.delegate.flatMap((handler) -> handler.handle(request, response));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal class used to manage the server and the {@link HttpHandler}, taking care
|
||||
* not to initialize the handler too early.
|
||||
|
|
@ -195,11 +227,14 @@ public class ReactiveWebServerApplicationContext
|
|||
|
||||
private final WebServer server;
|
||||
|
||||
private final boolean lazyInit;
|
||||
|
||||
private volatile HttpHandler handler;
|
||||
|
||||
private ServerManager(ReactiveWebServerFactory factory) {
|
||||
private ServerManager(ReactiveWebServerFactory factory, boolean lazyInit) {
|
||||
this.handler = this::handleUninitialized;
|
||||
this.server = factory.getWebServer(this);
|
||||
this.lazyInit = lazyInit;
|
||||
}
|
||||
|
||||
private Mono<Void> handleUninitialized(ServerHttpRequest request,
|
||||
|
|
@ -217,8 +252,9 @@ public class ReactiveWebServerApplicationContext
|
|||
return this.handler;
|
||||
}
|
||||
|
||||
public static ServerManager get(ReactiveWebServerFactory factory) {
|
||||
return new ServerManager(factory);
|
||||
public static ServerManager get(ReactiveWebServerFactory factory,
|
||||
boolean lazyInit) {
|
||||
return new ServerManager(factory, lazyInit);
|
||||
}
|
||||
|
||||
public static WebServer getWebServer(ServerManager manager) {
|
||||
|
|
@ -228,7 +264,9 @@ public class ReactiveWebServerApplicationContext
|
|||
public static void start(ServerManager manager,
|
||||
Supplier<HttpHandler> handlerSupplier) {
|
||||
if (manager != null && manager.server != null) {
|
||||
manager.handler = handlerSupplier.get();
|
||||
manager.handler = manager.lazyInit
|
||||
? new LazyHttpHandler(Mono.fromSupplier(handlerSupplier))
|
||||
: handlerSupplier.get();
|
||||
manager.server.start();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -218,6 +218,13 @@
|
|||
"description": "Mode used to display the banner when the application runs.",
|
||||
"defaultValue": "console"
|
||||
},
|
||||
{
|
||||
"name": "spring.main.lazy-initialization",
|
||||
"type": "java.lang.Boolean",
|
||||
"sourceType": "org.springframework.boot.SpringApplication",
|
||||
"description": "Whether initialization should be performed lazily.",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"name": "spring.main.show-banner",
|
||||
"type": "java.lang.Boolean",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import java.util.Iterator;
|
|||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
|
@ -1171,6 +1172,21 @@ public class SpringApplicationTests {
|
|||
.getBean("someBean")).isEqualTo("override");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lazyInitializationIsDisabledByDefault() {
|
||||
assertThat(new SpringApplication(LazyInitializationConfig.class)
|
||||
.run("--spring.main.web-application-type=none")
|
||||
.getBean(AtomicInteger.class)).hasValue(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lazyInitializationCanBeEnabled() {
|
||||
assertThat(new SpringApplication(LazyInitializationConfig.class)
|
||||
.run("--spring.main.web-application-type=none",
|
||||
"--spring.main.lazy-initialization=true")
|
||||
.getBean(AtomicInteger.class)).hasValue(0);
|
||||
}
|
||||
|
||||
private Condition<ConfigurableEnvironment> matchingPropertySource(
|
||||
final Class<?> propertySourceClass, final String name) {
|
||||
return new Condition<ConfigurableEnvironment>("has property source") {
|
||||
|
|
@ -1437,6 +1453,29 @@ public class SpringApplicationTests {
|
|||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class LazyInitializationConfig {
|
||||
|
||||
@Bean
|
||||
public AtomicInteger counter() {
|
||||
return new AtomicInteger(0);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public LazyBean lazyBean(AtomicInteger counter) {
|
||||
return new LazyBean(counter);
|
||||
}
|
||||
|
||||
static class LazyBean {
|
||||
|
||||
LazyBean(AtomicInteger counter) {
|
||||
counter.incrementAndGet();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class ExitStatusException extends RuntimeException
|
||||
implements ExitCodeGenerator {
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -29,7 +29,7 @@ import static org.springframework.web.reactive.function.server.RouterFunctions.r
|
|||
public class SampleWebFluxApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SampleWebFluxApplication.class);
|
||||
SpringApplication.run(SampleWebFluxApplication.class, args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
|
|
|||
Loading…
Reference in New Issue