mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-09-03 04:26:12 +08:00
Defer HttpHandler initialization
Update `ReactiveWebServerApplicationContext` so that the `HttpHandler` bean is not longer created from `onRefresh`, but is instead created only when the server starts. Prior to this commit, the WebFlux hander would cause early initialization of several beans, including Jackson Modules. Closes gh-14666
This commit is contained in:
parent
7afde2ba43
commit
bd9500290e
@ -16,6 +16,10 @@
|
||||
|
||||
package org.springframework.boot.web.reactive.context;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.boot.web.context.ConfigurableWebServerApplicationContext;
|
||||
@ -23,6 +27,8 @@ import org.springframework.boot.web.reactive.server.ReactiveWebServerFactory;
|
||||
import org.springframework.boot.web.server.WebServer;
|
||||
import org.springframework.context.ApplicationContextException;
|
||||
import org.springframework.http.server.reactive.HttpHandler;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@ -38,6 +44,8 @@ public class ReactiveWebServerApplicationContext
|
||||
|
||||
private volatile WebServer webServer;
|
||||
|
||||
private volatile DeferredHttpHandler httpHandler;
|
||||
|
||||
private String serverNamespace;
|
||||
|
||||
/**
|
||||
@ -78,6 +86,17 @@ public class ReactiveWebServerApplicationContext
|
||||
}
|
||||
}
|
||||
|
||||
private void createWebServer() {
|
||||
WebServer localServer = this.webServer;
|
||||
if (localServer == null) {
|
||||
DeferredHttpHandler localHandler = new DeferredHttpHandler(
|
||||
this::getHttpHandler);
|
||||
this.webServer = getWebServerFactory().getWebServer(localHandler);
|
||||
this.httpHandler = localHandler;
|
||||
}
|
||||
initPropertySources();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finishRefresh() {
|
||||
super.finishRefresh();
|
||||
@ -93,14 +112,6 @@ public class ReactiveWebServerApplicationContext
|
||||
stopAndReleaseReactiveWebServer();
|
||||
}
|
||||
|
||||
private void createWebServer() {
|
||||
WebServer localServer = this.webServer;
|
||||
if (localServer == null) {
|
||||
this.webServer = getWebServerFactory().getWebServer(getHttpHandler());
|
||||
}
|
||||
initPropertySources();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link WebServer} that was created by the context or {@code null} if
|
||||
* the server has not yet been created.
|
||||
@ -157,7 +168,12 @@ public class ReactiveWebServerApplicationContext
|
||||
|
||||
private WebServer startReactiveWebServer() {
|
||||
WebServer localServer = this.webServer;
|
||||
DeferredHttpHandler localHandler = this.httpHandler;
|
||||
if (localServer != null) {
|
||||
if (localHandler != null) {
|
||||
localHandler.initialize();
|
||||
this.httpHandler = null;
|
||||
}
|
||||
localServer.start();
|
||||
}
|
||||
return localServer;
|
||||
@ -186,4 +202,40 @@ public class ReactiveWebServerApplicationContext
|
||||
this.serverNamespace = serverNamespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link HttpHandler} that defers to a supplied handler which is initialized only
|
||||
* when the server starts.
|
||||
*/
|
||||
static class DeferredHttpHandler implements HttpHandler {
|
||||
|
||||
private Supplier<HttpHandler> factory;
|
||||
|
||||
private HttpHandler handler;
|
||||
|
||||
DeferredHttpHandler(Supplier<HttpHandler> factory) {
|
||||
this.factory = factory;
|
||||
this.handler = this::handleUninitialized;
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
this.handler = this.factory.get();
|
||||
}
|
||||
|
||||
private Mono<Void> handleUninitialized(ServerHttpRequest request,
|
||||
ServerHttpResponse response) {
|
||||
throw new IllegalStateException(
|
||||
"The HttpHandler has not yet been initialized");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
|
||||
return this.handler.handle(request, response);
|
||||
}
|
||||
|
||||
public HttpHandler getHandler() {
|
||||
return this.handler;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -18,11 +18,16 @@ package org.springframework.boot.web.reactive.context;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext.DeferredHttpHandler;
|
||||
import org.springframework.boot.web.reactive.context.config.ExampleReactiveWebServerApplicationConfiguration;
|
||||
import org.springframework.boot.web.reactive.server.MockReactiveWebServerFactory;
|
||||
import org.springframework.boot.web.reactive.server.ReactiveWebServerFactory;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.event.ApplicationEventMulticaster;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.context.event.SimpleApplicationEventMulticaster;
|
||||
import org.springframework.http.server.reactive.HttpHandler;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@ -80,11 +85,23 @@ public class AnnotationConfigReactiveWebServerApplicationContextTests {
|
||||
verifyContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void httpHandlerInitialization() {
|
||||
// gh-14666
|
||||
this.context = new AnnotationConfigReactiveWebServerApplicationContext(
|
||||
InitializationTestConfig.class);
|
||||
verifyContext();
|
||||
}
|
||||
|
||||
private void verifyContext() {
|
||||
MockReactiveWebServerFactory factory = this.context
|
||||
.getBean(MockReactiveWebServerFactory.class);
|
||||
HttpHandler httpHandler = this.context.getBean(HttpHandler.class);
|
||||
assertThat(factory.getWebServer().getHttpHandler()).isEqualTo(httpHandler);
|
||||
HttpHandler expectedHandler = this.context.getBean(HttpHandler.class);
|
||||
HttpHandler actualHandler = factory.getWebServer().getHttpHandler();
|
||||
if (actualHandler instanceof DeferredHttpHandler) {
|
||||
actualHandler = ((DeferredHttpHandler) actualHandler).getHandler();
|
||||
}
|
||||
assertThat(actualHandler).isEqualTo(expectedHandler);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ -107,4 +124,54 @@ public class AnnotationConfigReactiveWebServerApplicationContextTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class InitializationTestConfig {
|
||||
|
||||
private static boolean addedListener;
|
||||
|
||||
@Bean
|
||||
public ReactiveWebServerFactory webServerFactory() {
|
||||
return new MockReactiveWebServerFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public HttpHandler httpHander() {
|
||||
if (!addedListener) {
|
||||
throw new RuntimeException(
|
||||
"Handlers should be added after listeners, we're being initialized too early!");
|
||||
}
|
||||
return mock(HttpHandler.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Listener listener() {
|
||||
return new Listener();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ApplicationEventMulticaster applicationEventMulticaster() {
|
||||
return new SimpleApplicationEventMulticaster() {
|
||||
|
||||
@Override
|
||||
public void addApplicationListenerBean(String listenerBeanName) {
|
||||
super.addApplicationListenerBean(listenerBeanName);
|
||||
if ("listener".equals(listenerBeanName)) {
|
||||
addedListener = true;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
private static class Listener
|
||||
implements ApplicationListener<ContextRefreshedEvent> {
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ContextRefreshedEvent event) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user