提交 e638fef5 编写于 作者: A Arjen Poutsma

Add Servlet.fn integration with DispatcherServlet

This commit adds support for HandlerFunctions and RouterFunctions to
DispatcherServlet, in the form of a HandlerAdapter and HandlerMapping.

See gh-21490
上级 85082fc1
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-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.
......@@ -77,6 +77,8 @@ import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.function.support.HandlerFunctionAdapter;
import org.springframework.web.servlet.function.support.RouterFunctionMapping;
import org.springframework.web.servlet.handler.AbstractHandlerMapping;
import org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping;
import org.springframework.web.servlet.handler.ConversionServiceExposingInterceptor;
......@@ -474,6 +476,27 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv
return mapping;
}
/**
* Return a {@link RouterFunctionMapping} ordered at 3 to map
* {@linkplain org.springframework.web.servlet.function.RouterFunction router functions}.
* Consider overriding one of these other more fine-grained methods:
* <ul>
* <li>{@link #addInterceptors} for adding handler interceptors.
* <li>{@link #addCorsMappings} to configure cross origin requests processing.
* <li>{@link #configureMessageConverters} for adding custom message converters.
* </ul>
* @since 5.2
*/
@Bean
public RouterFunctionMapping routerFunctionMapping() {
RouterFunctionMapping mapping = new RouterFunctionMapping();
mapping.setOrder(3);
mapping.setInterceptors(getInterceptors());
mapping.setCorsConfigurations(getCorsConfigurations());
mapping.setMessageConverters(getMessageConverters());
return mapping;
}
/**
* Return a handler mapping ordered at Integer.MAX_VALUE-1 with mapped
* resource handlers. To configure resource handling, override
......@@ -593,6 +616,16 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv
return new RequestMappingHandlerAdapter();
}
/**
* Returns a {@link HandlerFunctionAdapter} for processing requests through
* {@linkplain org.springframework.web.servlet.function.HandlerFunction handler functions}.
* @since 5.2
*/
@Bean
public HandlerFunctionAdapter handlerFunctionAdapter() {
return new HandlerFunctionAdapter();
}
/**
* Return the {@link ConfigurableWebBindingInitializer} to use for
* initializing all {@link WebDataBinder} instances.
......
/*
* Copyright 2002-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
*
* http://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.web.servlet.function.support;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.core.Ordered;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.function.HandlerFunction;
import org.springframework.web.servlet.function.RouterFunctions;
import org.springframework.web.servlet.function.ServerRequest;
import org.springframework.web.servlet.function.ServerResponse;
/**
* {@code HandlerAdapter} implementation that supports {@link HandlerFunction}s.
*
* @author Arjen Poutsma
* @since 5.2
*/
public class HandlerFunctionAdapter implements HandlerAdapter, Ordered {
private int order = Ordered.LOWEST_PRECEDENCE;
/**
* Specify the order value for this HandlerAdapter bean.
* <p>The default value is {@code Ordered.LOWEST_PRECEDENCE}, meaning non-ordered.
* @see org.springframework.core.Ordered#getOrder()
*/
public void setOrder(int order) {
this.order = order;
}
@Override
public int getOrder() {
return this.order;
}
@Override
public boolean supports(Object handler) {
return handler instanceof HandlerFunction;
}
@Nullable
@Override
public ModelAndView handle(HttpServletRequest servletRequest,
HttpServletResponse servletResponse,
Object handler) throws Exception {
HandlerFunction<?> handlerFunction = (HandlerFunction<?>) handler;
ServerRequest serverRequest = getServerRequest(servletRequest);
ServerResponse serverResponse = handlerFunction.handle(serverRequest);
return serverResponse.writeTo(servletRequest, servletResponse,
new ServerRequestContext(serverRequest));
}
private ServerRequest getServerRequest(HttpServletRequest servletRequest) {
ServerRequest serverRequest =
(ServerRequest) servletRequest.getAttribute(RouterFunctions.REQUEST_ATTRIBUTE);
Assert.state(serverRequest != null, () -> "Required attribute '" +
RouterFunctions.REQUEST_ATTRIBUTE + "' is missing");
return serverRequest;
}
@Override
public long getLastModified(HttpServletRequest request, Object handler) {
return -1L;
}
private static class ServerRequestContext implements ServerResponse.Context {
private final ServerRequest serverRequest;
public ServerRequestContext(ServerRequest serverRequest) {
this.serverRequest = serverRequest;
}
@Override
public List<HttpMessageConverter<?>> messageConverters() {
return this.serverRequest.messageConverters();
}
}
}
/*
* Copyright 2002-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
*
* http://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.web.servlet.function.support;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
import org.springframework.http.converter.xml.SourceHttpMessageConverter;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
import org.springframework.web.servlet.function.RouterFunction;
import org.springframework.web.servlet.function.RouterFunctions;
import org.springframework.web.servlet.function.ServerRequest;
import org.springframework.web.servlet.handler.AbstractHandlerMapping;
/**
* {@code HandlerMapping} implementation that supports {@link RouterFunction}s.
* <p>If no {@link RouterFunction} is provided at
* {@linkplain #RouterFunctionMapping(RouterFunction) construction time}, this mapping will detect
* all router functions in the application context, and consult them in
* {@linkplain org.springframework.core.annotation.Order order}.
*
* @author Arjen Poutsma
* @since 5.2
*/
public class RouterFunctionMapping extends AbstractHandlerMapping implements InitializingBean {
@Nullable
private RouterFunction<?> routerFunction;
private List<HttpMessageConverter<?>> messageConverters = Collections.emptyList();
private boolean detectHandlerFunctionsInAncestorContexts = false;
/**
* Create an empty {@code RouterFunctionMapping}.
* <p>If this constructor is used, this mapping will detect all {@link RouterFunction} instances
* available in the application context.
*/
public RouterFunctionMapping() {
}
/**
* Create a {@code RouterFunctionMapping} with the given {@link RouterFunction}.
* <p>If this constructor is used, no application context detection will occur.
* @param routerFunction the router function to use for mapping
*/
public RouterFunctionMapping(RouterFunction<?> routerFunction) {
this.routerFunction = routerFunction;
}
/**
* Set the router function to map to.
* <p>If this property is used, no application context detection will occur.
*/
public void setRouterFunction(@Nullable RouterFunction<?> routerFunction) {
this.routerFunction = routerFunction;
}
/**
* Return the configured {@link RouterFunction}.
* <p><strong>Note:</strong> When router functions are detected from the
* ApplicationContext, this method may return {@code null} if invoked
* prior to {@link #afterPropertiesSet()}.
* @return the router function or {@code null}
*/
@Nullable
public RouterFunction<?> getRouterFunction() {
return this.routerFunction;
}
public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
this.messageConverters = messageConverters;
}
/**
* Set whether to detect handler functions in ancestor ApplicationContexts.
* <p>Default is "false": Only handler functions in the current ApplicationContext
* will be detected, i.e. only in the context that this HandlerMapping itself
* is defined in (typically the current DispatcherServlet's context).
* <p>Switch this flag on to detect handler beans in ancestor contexts
* (typically the Spring root WebApplicationContext) as well.
*/
public void setDetectHandlerFunctionsInAncestorContexts(boolean detectHandlerFunctionsInAncestorContexts) {
this.detectHandlerFunctionsInAncestorContexts = detectHandlerFunctionsInAncestorContexts;
}
@Override
public void afterPropertiesSet() throws Exception {
if (this.routerFunction == null) {
initRouterFunction();
}
if (CollectionUtils.isEmpty(this.messageConverters)) {
initMessageConverters();
}
}
/**
* Detect a all {@linkplain RouterFunction router functions} in the
* current application context.
*/
@SuppressWarnings({"unchecked", "rawtypes"})
private void initRouterFunction() {
ApplicationContext applicationContext = obtainApplicationContext();
Map<String, RouterFunction> beans =
(this.detectHandlerFunctionsInAncestorContexts ?
BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, RouterFunction.class) :
applicationContext.getBeansOfType(RouterFunction.class));
List<RouterFunction> routerFunctions = new ArrayList<>(beans.values());
if (!CollectionUtils.isEmpty(routerFunctions) && logger.isInfoEnabled()) {
routerFunctions.forEach(routerFunction -> logger.info("Mapped " + routerFunction));
}
this.routerFunction = routerFunctions.stream()
.reduce(RouterFunction::andOther)
.orElse(null);
}
/**
* Initializes a default set of {@linkplain HttpMessageConverter message converters}.
*/
private void initMessageConverters() {
List<HttpMessageConverter<?>> messageConverters = new ArrayList<>(4);
messageConverters.add(new ByteArrayHttpMessageConverter());
StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
stringHttpMessageConverter.setWriteAcceptCharset(false); // see SPR-7316
messageConverters.add(stringHttpMessageConverter);
try {
messageConverters.add(new SourceHttpMessageConverter<>());
}
catch (Error err) {
// Ignore when no TransformerFactory implementation is available
}
messageConverters.add(new AllEncompassingFormHttpMessageConverter());
this.messageConverters = messageConverters;
}
@Nullable
@Override
protected Object getHandlerInternal(@NotNull HttpServletRequest servletRequest) throws Exception {
if (this.routerFunction != null) {
ServerRequest request = ServerRequest.create(servletRequest, this.messageConverters);
servletRequest.setAttribute(RouterFunctions.REQUEST_ATTRIBUTE, request);
return this.routerFunction.route(request).orElse(null);
}
else {
return null;
}
}
}
/*
* Copyright 2002-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
*
* http://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.
*/
/**
* Classes supporting the {@code org.springframework.web.servlet.function} package.
* Contains a {@code HandlerAdapter} that supports {@code HandlerFunction}s,
* and a {@code HandlerMapping} that supports {@code RouterFunction}s.
*/
@NonNullApi
@NonNullFields
package org.springframework.web.servlet.function.support;
import org.springframework.lang.NonNullApi;
import org.springframework.lang.NonNullFields;
......@@ -7,11 +7,14 @@ org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
org.springframework.web.servlet.function.support.RouterFunctionMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
org.springframework.web.servlet.function.support.HandlerFunctionAdapter
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册