在spring-webflux中已经实现了两条腿走路。采用了函数式和@Controller两种模式。在5.2版本中,spring-mvc也支持了函数式端点。
@RequiredArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
@Configuration
@Slf4j
public class FunctionalEndpointConfig {
FunctionalEndpoint functionalEndpoint;
@Bean
public RouterFunction<ServerResponse> functionalEndpointRouterFunction() {
return RouterFunctions.route()
.GET("/", functionalEndpoint::index)
.GET("/me", functionalEndpoint::me)
.POST("/post-json", functionalEndpoint::postJson)
.POST("/post-form", functionalEndpoint::postForm)
.GET("/redirect", functionalEndpoint::redirect)
.build();
}
@Bean
public RouterFunction<ServerResponse> lambdaRouterFunction() {
return RouterFunctions.route()
.POST("/users", request -> {
return ServerResponse.ok().build();
})
.before(serverRequest -> {
// 最先执行,以便于包装 serverRequest
log.info("before");
return serverRequest;
})
.after((serverRequest, serverResponse) -> {
// 最后执行,以便于包装serverResponse
log.info("after");
return serverResponse;
})
.filter((request, next) -> {
// 中间执行,此处获取到的request是在before中增强过的
// 与servlet的filter类似,实现过滤的功能
log.info("filter");
return next.handle(request);
})
.build();
}
@Bean
public RouterFunction<ServerResponse> errorRouterFunction() {
return RouterFunctions.route()
.POST("/errors", request -> {
throw new IllegalAccessException();
})
// 错误处理,处理所有异常
// 如果此处不处理异常,异常处理会进入spring-mvc的标准处理流程
.onError(Exception.class, (throwable, serverRequest) -> {
return ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
})
.build();
}
}
@Component
@Slf4j
public class FunctionalEndpoint {
public ServerResponse index(ServerRequest serverRequest) {
val headers = serverRequest.headers();
val httpMethod = serverRequest.method();
val methodName = serverRequest.methodName();
val cookies = serverRequest.cookies();
val params = serverRequest.params();
val stringStringMap = serverRequest.pathVariables();
val httpServletRequest = serverRequest.servletRequest();
val uri = serverRequest.uri();
val principal = serverRequest.principal();
val inetSocketAddress = serverRequest.remoteAddress();
log.info("headers:{}", headers);
log.info("httpMethod:{}", httpMethod);
log.info("methodName:{}", methodName);
log.info("cookies:{}", cookies);
log.info("params:{}", params);
log.info("stringStringMap:{}", stringStringMap);
log.info("httpServletRequest:{}", httpServletRequest);
log.info("uri:{}", uri);
log.info("principal:{}", principal);
log.info("inetSocketAddress:{}", inetSocketAddress);
return ServerResponse.ok().body(Map.of("name", "name"));
}
public ServerResponse me(ServerRequest serverRequest) {
val stringObjectHashMap = new HashMap<String, Object>();
stringObjectHashMap.put("id", 1);
stringObjectHashMap.put("username", 123);
return ServerResponse.ok().body(stringObjectHashMap);
}
public ServerResponse postJson(ServerRequest serverRequest) throws ServletException, IOException {
// 接收json数据
val body = serverRequest.body(new ParameterizedTypeReference<Map<String, Object>>() {
});
return ServerResponse.ok().body(body);
}
public ServerResponse postForm(ServerRequest serverRequest) throws ServletException, IOException {
// 接收form表单数据
val body = serverRequest.body(new ParameterizedTypeReference<MultiValueMap<String, String>>() {
});
val stringStringMap = body.toSingleValueMap();
return ServerResponse.ok().body(stringStringMap);
}
/** 重定向 */
public ServerResponse redirect(ServerRequest serverRequest) throws ServletException, IOException {
// 重定向到 /
val path = serverRequest.uriBuilder().replacePath("/");
URI build = path.build();
return ServerResponse.status(HttpStatus.FOUND).location(build).build();
}
}
RouterFunctionMapping 检索RouterHandler
public class RouterFunctionMapping extends AbstractHandlerMapping implements InitializingBean {
@Nullable
private RouterFunction<?> routerFunction;
private List<HttpMessageConverter<?>> messageConverters = Collections.emptyList();
private boolean detectHandlerFunctionsInAncestorContexts = false;
public RouterFunctionMapping() {
}
public RouterFunctionMapping(RouterFunction<?> routerFunction) {
this.routerFunction = routerFunction;
}
public void setRouterFunction(@Nullable RouterFunction<?> routerFunction) {
this.routerFunction = routerFunction;
}
@Nullable
public RouterFunction<?> getRouterFunction() {
return this.routerFunction;
}
public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
this.messageConverters = messageConverters;
}
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();
}
}
/**
* 收集所有RouterFunction
*/
@SuppressWarnings({"rawtypes", "unchecked"})
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);
}
private void initMessageConverters() {
List<HttpMessageConverter<?>> messageConverters = new ArrayList<>(4);
messageConverters.add(new ByteArrayHttpMessageConverter());
messageConverters.add(new StringHttpMessageConverter());
try {
messageConverters.add(new SourceHttpMessageConverter<>());
}
catch (Error err) {
// Ignore when no TransformerFactory implementation is available
}
messageConverters.add(new AllEncompassingFormHttpMessageConverter());
this.messageConverters = messageConverters;
}
/**
* 匹配RouterFunction
*/
@Nullable
@Override
protected Object getHandlerInternal(HttpServletRequest servletRequest) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(servletRequest);
servletRequest.setAttribute(LOOKUP_PATH, lookupPath);
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;
}
}
}
HandlerFunctionAdapter 负责执行HandlerFunction
public class HandlerFunctionAdapter implements HandlerAdapter, Ordered {
private int order = Ordered.LOWEST_PRECEDENCE;
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();
}
}
}
public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {
@Bean
public RouterFunctionMapping routerFunctionMapping(
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
RouterFunctionMapping mapping = new RouterFunctionMapping();
mapping.setOrder(3);
mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
mapping.setCorsConfigurations(getCorsConfigurations());
mapping.setMessageConverters(getMessageConverters());
return mapping;
}
/**
* 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();
}
}
基于@Controller模式的端点那么好用?为什么要用函数式端点呢?