ApplicationContext-based HttpHandler setup

This commit adds support for detecting the target WebHandler along with
WebFilters, WebExceptionHandlers, and other spring-web reactive
strategies in an ApplicationContext.

WebReactiveConfigurationSupport has @Bean factory methods for
DispatcherHandler and ResponseStatusExceptionHandler.

WebHttpHandlerBuilder has a static factory method that initializes the
builder from an ApplicationContext. This method is also used in the
DispatcherHandler#toHttpHandler(ApplicationContext) shortcut method.

Issue: SPR-14837
This commit is contained in:
Rossen Stoyanchev 2016-10-21 18:02:16 -04:00
parent 45fc449550
commit 33dbbce182
4 changed files with 95 additions and 11 deletions

View File

@ -36,6 +36,7 @@ import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebHandler;
import org.springframework.web.server.adapter.HttpWebHandlerAdapter;
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
/**
* Central dispatcher for HTTP request handlers/controllers. Dispatches to registered
@ -170,7 +171,14 @@ public class DispatcherHandler implements WebHandler, ApplicationContextAware {
* Expose a dispatcher-based {@link HttpHandler} for the given application context,
* typically for direct registration with an engine adapter such as
* {@link org.springframework.http.server.reactive.ServletHttpHandlerAdapter}.
* @param applicationContext the application context to find the handler beans in
*
* <p>Delegates to {@link WebHttpHandlerBuilder#applicationContext} that
* detects the target {@link DispatcherHandler} along with
* {@link org.springframework.web.server.WebFilter}s, and
* {@link org.springframework.web.server.WebExceptionHandler}s in the given
* ApplicationContext.
*
* @param context the application context to find the handler beans in
* @see #DispatcherHandler(ApplicationContext)
* @see HttpWebHandlerAdapter
* @see org.springframework.http.server.reactive.ServletHttpHandlerAdapter
@ -178,8 +186,8 @@ public class DispatcherHandler implements WebHandler, ApplicationContextAware {
* @see org.springframework.http.server.reactive.RxNettyHttpHandlerAdapter
* @see org.springframework.http.server.reactive.UndertowHttpHandlerAdapter
*/
public static HttpHandler toHttpHandler(ApplicationContext applicationContext) {
return new HttpWebHandlerAdapter(new DispatcherHandler(applicationContext));
public static HttpHandler toHttpHandler(ApplicationContext context) {
return WebHttpHandlerBuilder.applicationContext(context).build();
}
}

View File

@ -61,6 +61,7 @@ import org.springframework.validation.Validator;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.reactive.DispatcherHandler;
import org.springframework.web.reactive.HandlerMapping;
import org.springframework.web.reactive.accept.CompositeContentTypeResolver;
import org.springframework.web.reactive.accept.RequestedContentTypeResolverBuilder;
@ -74,6 +75,8 @@ import org.springframework.web.reactive.result.method.annotation.ResponseEntityR
import org.springframework.web.reactive.result.view.ViewResolutionResultHandler;
import org.springframework.web.reactive.result.view.ViewResolver;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebExceptionHandler;
import org.springframework.web.server.handler.ResponseStatusExceptionHandler;
/**
* The main class for Spring Web Reactive configuration.
@ -116,6 +119,16 @@ public class WebReactiveConfigurationSupport implements ApplicationContextAware
}
@Bean
public DispatcherHandler webHandler() {
return new DispatcherHandler();
}
@Bean
public WebExceptionHandler responseStatusExceptionHandler() {
return new ResponseStatusExceptionHandler();
}
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();

View File

@ -29,9 +29,7 @@ import org.springframework.http.ResponseEntity;
import org.springframework.http.server.reactive.AbstractHttpHandlerIntegrationTests;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.DispatcherHandler;
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
import org.springframework.web.server.handler.ResponseStatusExceptionHandler;
import static org.springframework.http.RequestEntity.get;
import static org.springframework.http.RequestEntity.options;
@ -53,10 +51,7 @@ public abstract class AbstractRequestMappingIntegrationTests extends AbstractHtt
protected HttpHandler createHttpHandler() {
this.restTemplate = initRestTemplate();
this.applicationContext = initApplicationContext();
return WebHttpHandlerBuilder
.webHandler(new DispatcherHandler(this.applicationContext))
.exceptionHandlers(new ResponseStatusExceptionHandler())
.build();
return WebHttpHandlerBuilder.applicationContext(this.applicationContext).build();
}
protected abstract ApplicationContext initApplicationContext();

View File

@ -17,8 +17,13 @@ package org.springframework.web.server.adapter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
@ -28,6 +33,7 @@ import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebHandler;
import org.springframework.web.server.handler.ExceptionHandlingWebHandler;
import org.springframework.web.server.handler.FilteringWebHandler;
import org.springframework.web.server.session.DefaultWebSessionManager;
import org.springframework.web.server.session.WebSessionManager;
/**
@ -52,6 +58,13 @@ import org.springframework.web.server.session.WebSessionManager;
*/
public class WebHttpHandlerBuilder {
/** Well-known name for the target WebHandler in the bean factory. */
public static final String WEB_HANDLER_BEAN_NAME = "webHandler";
/** Well-known name for the WebSessionManager in the bean factory. */
public static final String WEB_SESSION_MANAGER_BEAN_NAME = "webSessionManager";
private final WebHandler targetHandler;
private final List<WebFilter> filters = new ArrayList<>();
@ -74,11 +87,66 @@ public class WebHttpHandlerBuilder {
/**
* Factory method to create a new builder instance.
* @param webHandler the target handler for the request
* @return the prepared builder
*/
public static WebHttpHandlerBuilder webHandler(WebHandler webHandler) {
return new WebHttpHandlerBuilder(webHandler);
}
/**
* Factory method to create a new builder instance by detecting beans in an
* {@link ApplicationContext}. The following are detected:
* <ul>
* <li>{@link WebHandler} [1] -- looked up by the name
* {@link #WEB_HANDLER_BEAN_NAME}.
* <li>{@link WebFilter} [0..N] -- detected by type and ordered,
* see {@link AnnotationAwareOrderComparator}.
* <li>{@link WebExceptionHandler} [0..N] -- detected by type and
* ordered.
* <li>{@link WebSessionManager} [0..1] -- looked up by the name
* {@link #WEB_SESSION_MANAGER_BEAN_NAME}.
* </ul>
* @param context the application context to use for the lookup
* @return the prepared builder
*/
public static WebHttpHandlerBuilder applicationContext(ApplicationContext context) {
// Target WebHandler
WebHttpHandlerBuilder builder = new WebHttpHandlerBuilder(
context.getBean(WEB_HANDLER_BEAN_NAME, WebHandler.class));
// WebFilter...
Collection<WebFilter> filters = BeanFactoryUtils.beansOfTypeIncludingAncestors(
context, WebFilter.class, true, false).values();
WebFilter[] sortedFilters = filters.toArray(new WebFilter[filters.size()]);
AnnotationAwareOrderComparator.sort(sortedFilters);
builder.filters(sortedFilters);
// WebExceptionHandler...
Collection<WebExceptionHandler> handlers = BeanFactoryUtils.beansOfTypeIncludingAncestors(
context, WebExceptionHandler.class, true, false).values();
WebExceptionHandler[] sortedHandlers = handlers.toArray(new WebExceptionHandler[handlers.size()]);
AnnotationAwareOrderComparator.sort(sortedHandlers);
builder.exceptionHandlers(sortedHandlers);
// WebSessionManager
try {
builder.sessionManager(
context.getBean(WEB_SESSION_MANAGER_BEAN_NAME, WebSessionManager.class));
}
catch (NoSuchBeanDefinitionException ex) {
// Fall back on default
}
return builder;
}
/**
* Add the given filters to use for processing requests.
@ -104,8 +172,8 @@ public class WebHttpHandlerBuilder {
/**
* Configure the {@link WebSessionManager} to set on the
* {@link ServerWebExchange WebServerExchange}
* created for each HTTP request.
* {@link ServerWebExchange WebServerExchange}.
* <p>By default {@link DefaultWebSessionManager} is used.
* @param sessionManager the session manager
* @see HttpWebHandlerAdapter#setSessionManager(WebSessionManager)
*/