|
|
|
@ -1,5 +1,5 @@ |
|
|
|
/* |
|
|
|
/* |
|
|
|
* Copyright 2002-2017 the original author or authors. |
|
|
|
* Copyright 2002-2018 the original author or authors. |
|
|
|
* |
|
|
|
* |
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
|
|
* you may not use this file except in compliance with the License. |
|
|
|
* you may not use this file except in compliance with the License. |
|
|
|
@ -24,8 +24,6 @@ import java.util.List; |
|
|
|
import java.util.Map; |
|
|
|
import java.util.Map; |
|
|
|
import java.util.Set; |
|
|
|
import java.util.Set; |
|
|
|
import java.util.concurrent.ConcurrentHashMap; |
|
|
|
import java.util.concurrent.ConcurrentHashMap; |
|
|
|
import java.util.function.Function; |
|
|
|
|
|
|
|
import java.util.function.Supplier; |
|
|
|
|
|
|
|
import java.util.stream.Collectors; |
|
|
|
import java.util.stream.Collectors; |
|
|
|
|
|
|
|
|
|
|
|
import org.apache.commons.logging.Log; |
|
|
|
import org.apache.commons.logging.Log; |
|
|
|
@ -40,7 +38,6 @@ import org.springframework.core.annotation.AnnotationUtils; |
|
|
|
import org.springframework.http.codec.HttpMessageReader; |
|
|
|
import org.springframework.http.codec.HttpMessageReader; |
|
|
|
import org.springframework.lang.Nullable; |
|
|
|
import org.springframework.lang.Nullable; |
|
|
|
import org.springframework.util.Assert; |
|
|
|
import org.springframework.util.Assert; |
|
|
|
import org.springframework.util.CollectionUtils; |
|
|
|
|
|
|
|
import org.springframework.util.ReflectionUtils; |
|
|
|
import org.springframework.util.ReflectionUtils; |
|
|
|
import org.springframework.web.bind.annotation.InitBinder; |
|
|
|
import org.springframework.web.bind.annotation.InitBinder; |
|
|
|
import org.springframework.web.bind.annotation.ModelAttribute; |
|
|
|
import org.springframework.web.bind.annotation.ModelAttribute; |
|
|
|
@ -53,7 +50,7 @@ import org.springframework.web.reactive.result.method.InvocableHandlerMethod; |
|
|
|
import org.springframework.web.reactive.result.method.SyncHandlerMethodArgumentResolver; |
|
|
|
import org.springframework.web.reactive.result.method.SyncHandlerMethodArgumentResolver; |
|
|
|
import org.springframework.web.reactive.result.method.SyncInvocableHandlerMethod; |
|
|
|
import org.springframework.web.reactive.result.method.SyncInvocableHandlerMethod; |
|
|
|
|
|
|
|
|
|
|
|
import static org.springframework.core.MethodIntrospector.selectMethods; |
|
|
|
import static org.springframework.core.MethodIntrospector.*; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Package-private class to assist {@link RequestMappingHandlerAdapter} with |
|
|
|
* Package-private class to assist {@link RequestMappingHandlerAdapter} with |
|
|
|
@ -102,81 +99,112 @@ class ControllerMethodResolver { |
|
|
|
private final Map<Class<?>, SessionAttributesHandler> sessionAttributesHandlerCache = new ConcurrentHashMap<>(64); |
|
|
|
private final Map<Class<?>, SessionAttributesHandler> sessionAttributesHandlerCache = new ConcurrentHashMap<>(64); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ControllerMethodResolver(ArgumentResolverConfigurer argumentResolvers, |
|
|
|
ControllerMethodResolver(ArgumentResolverConfigurer customResolvers, ReactiveAdapterRegistry reactiveRegistry, |
|
|
|
List<HttpMessageReader<?>> messageReaders, ReactiveAdapterRegistry reactiveRegistry, |
|
|
|
ConfigurableApplicationContext context, List<HttpMessageReader<?>> readers) { |
|
|
|
ConfigurableApplicationContext context) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Assert.notNull(argumentResolvers, "ArgumentResolverConfigurer is required"); |
|
|
|
Assert.notNull(customResolvers, "ArgumentResolverConfigurer is required"); |
|
|
|
Assert.notNull(messageReaders, "'messageReaders' is required"); |
|
|
|
Assert.notNull(readers, "'messageReaders' is required"); |
|
|
|
Assert.notNull(reactiveRegistry, "ReactiveAdapterRegistry is required"); |
|
|
|
Assert.notNull(reactiveRegistry, "ReactiveAdapterRegistry is required"); |
|
|
|
Assert.notNull(context, "ApplicationContext is required"); |
|
|
|
Assert.notNull(context, "ApplicationContext is required"); |
|
|
|
|
|
|
|
|
|
|
|
ArgumentResolverRegistrar registrar; |
|
|
|
this.initBinderResolvers = initBinderResolvers(customResolvers, reactiveRegistry, context); |
|
|
|
|
|
|
|
this.modelAttributeResolvers = modelMethodResolvers(customResolvers, reactiveRegistry, context); |
|
|
|
|
|
|
|
this.requestMappingResolvers = requestMappingResolvers(customResolvers, reactiveRegistry, context, readers); |
|
|
|
|
|
|
|
this.exceptionHandlerResolvers = exceptionHandlerResolvers(customResolvers, reactiveRegistry, context); |
|
|
|
|
|
|
|
this.reactiveAdapterRegistry = reactiveRegistry; |
|
|
|
|
|
|
|
|
|
|
|
registrar = ArgumentResolverRegistrar.configurer(argumentResolvers).basic(); |
|
|
|
initControllerAdviceCaches(context); |
|
|
|
addResolversTo(registrar, reactiveRegistry, context); |
|
|
|
} |
|
|
|
this.initBinderResolvers = registrar.getSyncResolvers(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
registrar = ArgumentResolverRegistrar.configurer(argumentResolvers).modelAttributeSupport(); |
|
|
|
private List<SyncHandlerMethodArgumentResolver> initBinderResolvers( |
|
|
|
addResolversTo(registrar, reactiveRegistry, context); |
|
|
|
ArgumentResolverConfigurer customResolvers, ReactiveAdapterRegistry reactiveRegistry, |
|
|
|
this.modelAttributeResolvers = registrar.getResolvers(); |
|
|
|
ConfigurableApplicationContext context) { |
|
|
|
|
|
|
|
|
|
|
|
registrar = ArgumentResolverRegistrar.configurer(argumentResolvers).fullSupport(messageReaders); |
|
|
|
return initResolvers(customResolvers, reactiveRegistry, context, false, Collections.emptyList()).stream() |
|
|
|
addResolversTo(registrar, reactiveRegistry, context); |
|
|
|
.filter(resolver -> resolver instanceof SyncHandlerMethodArgumentResolver) |
|
|
|
this.requestMappingResolvers = registrar.getResolvers(); |
|
|
|
.map(resolver -> (SyncHandlerMethodArgumentResolver) resolver) |
|
|
|
|
|
|
|
.collect(Collectors.toList()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
registrar = ArgumentResolverRegistrar.configurer(argumentResolvers).basic(); |
|
|
|
private static List<HandlerMethodArgumentResolver> modelMethodResolvers( |
|
|
|
addResolversTo(registrar, reactiveRegistry, context); |
|
|
|
ArgumentResolverConfigurer customResolvers, ReactiveAdapterRegistry reactiveRegistry, |
|
|
|
this.exceptionHandlerResolvers = registrar.getResolvers(); |
|
|
|
ConfigurableApplicationContext context) { |
|
|
|
|
|
|
|
|
|
|
|
this.reactiveAdapterRegistry = reactiveRegistry; |
|
|
|
return initResolvers(customResolvers, reactiveRegistry, context, true, Collections.emptyList()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
initControllerAdviceCaches(context); |
|
|
|
private static List<HandlerMethodArgumentResolver> requestMappingResolvers( |
|
|
|
|
|
|
|
ArgumentResolverConfigurer customResolvers, ReactiveAdapterRegistry reactiveRegistry, |
|
|
|
|
|
|
|
ConfigurableApplicationContext context, List<HttpMessageReader<?>> readers) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return initResolvers(customResolvers, reactiveRegistry, context, true, readers); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static List<HandlerMethodArgumentResolver> exceptionHandlerResolvers( |
|
|
|
|
|
|
|
ArgumentResolverConfigurer customResolvers, ReactiveAdapterRegistry reactiveRegistry, |
|
|
|
|
|
|
|
ConfigurableApplicationContext context) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return initResolvers(customResolvers, reactiveRegistry, context, false, Collections.emptyList()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void addResolversTo(ArgumentResolverRegistrar registrar, |
|
|
|
private static List<HandlerMethodArgumentResolver> initResolvers(ArgumentResolverConfigurer customResolvers, |
|
|
|
ReactiveAdapterRegistry reactiveRegistry, ConfigurableApplicationContext context) { |
|
|
|
ReactiveAdapterRegistry reactiveRegistry, ConfigurableApplicationContext context, |
|
|
|
|
|
|
|
boolean supportDataBinding, List<HttpMessageReader<?>> readers) { |
|
|
|
|
|
|
|
|
|
|
|
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); |
|
|
|
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); |
|
|
|
|
|
|
|
boolean requestMappingMethod = !readers.isEmpty() && supportDataBinding; |
|
|
|
|
|
|
|
|
|
|
|
// Annotation-based...
|
|
|
|
// Annotation-based...
|
|
|
|
registrar.add(new RequestParamMethodArgumentResolver(beanFactory, reactiveRegistry, false)); |
|
|
|
List<HandlerMethodArgumentResolver> result = new ArrayList<>(); |
|
|
|
registrar.add(new RequestParamMapMethodArgumentResolver(reactiveRegistry)); |
|
|
|
result.add(new RequestParamMethodArgumentResolver(beanFactory, reactiveRegistry, false)); |
|
|
|
registrar.add(new PathVariableMethodArgumentResolver(beanFactory, reactiveRegistry)); |
|
|
|
result.add(new RequestParamMapMethodArgumentResolver(reactiveRegistry)); |
|
|
|
registrar.add(new PathVariableMapMethodArgumentResolver(reactiveRegistry)); |
|
|
|
result.add(new PathVariableMethodArgumentResolver(beanFactory, reactiveRegistry)); |
|
|
|
registrar.add(new MatrixVariableMethodArgumentResolver(beanFactory, reactiveRegistry)); |
|
|
|
result.add(new PathVariableMapMethodArgumentResolver(reactiveRegistry)); |
|
|
|
registrar.add(new MatrixVariableMapMethodArgumentResolver(reactiveRegistry)); |
|
|
|
result.add(new MatrixVariableMethodArgumentResolver(beanFactory, reactiveRegistry)); |
|
|
|
registrar.addIfRequestBody(readers -> new RequestBodyArgumentResolver(readers, reactiveRegistry)); |
|
|
|
result.add(new MatrixVariableMapMethodArgumentResolver(reactiveRegistry)); |
|
|
|
registrar.addIfRequestBody(readers -> new RequestPartMethodArgumentResolver(readers, reactiveRegistry)); |
|
|
|
if (!readers.isEmpty()) { |
|
|
|
registrar.addIfModelAttribute(() -> new ModelAttributeMethodArgumentResolver(reactiveRegistry, false)); |
|
|
|
result.add(new RequestBodyArgumentResolver(readers, reactiveRegistry)); |
|
|
|
registrar.add(new RequestHeaderMethodArgumentResolver(beanFactory, reactiveRegistry)); |
|
|
|
result.add(new RequestPartMethodArgumentResolver(readers, reactiveRegistry)); |
|
|
|
registrar.add(new RequestHeaderMapMethodArgumentResolver(reactiveRegistry)); |
|
|
|
} |
|
|
|
registrar.add(new CookieValueMethodArgumentResolver(beanFactory, reactiveRegistry)); |
|
|
|
if (supportDataBinding) { |
|
|
|
registrar.add(new ExpressionValueMethodArgumentResolver(beanFactory, reactiveRegistry)); |
|
|
|
result.add(new ModelAttributeMethodArgumentResolver(reactiveRegistry, false)); |
|
|
|
registrar.add(new SessionAttributeMethodArgumentResolver(beanFactory, reactiveRegistry)); |
|
|
|
} |
|
|
|
registrar.add(new RequestAttributeMethodArgumentResolver(beanFactory, reactiveRegistry)); |
|
|
|
result.add(new RequestHeaderMethodArgumentResolver(beanFactory, reactiveRegistry)); |
|
|
|
|
|
|
|
result.add(new RequestHeaderMapMethodArgumentResolver(reactiveRegistry)); |
|
|
|
|
|
|
|
result.add(new CookieValueMethodArgumentResolver(beanFactory, reactiveRegistry)); |
|
|
|
|
|
|
|
result.add(new ExpressionValueMethodArgumentResolver(beanFactory, reactiveRegistry)); |
|
|
|
|
|
|
|
result.add(new SessionAttributeMethodArgumentResolver(beanFactory, reactiveRegistry)); |
|
|
|
|
|
|
|
result.add(new RequestAttributeMethodArgumentResolver(beanFactory, reactiveRegistry)); |
|
|
|
|
|
|
|
|
|
|
|
// Type-based...
|
|
|
|
// Type-based...
|
|
|
|
registrar.addIfRequestBody(readers -> new HttpEntityArgumentResolver(readers, reactiveRegistry)); |
|
|
|
if (!readers.isEmpty()) { |
|
|
|
registrar.add(new ModelArgumentResolver(reactiveRegistry)); |
|
|
|
result.add(new HttpEntityArgumentResolver(readers, reactiveRegistry)); |
|
|
|
registrar.addIfModelAttribute(() -> new ErrorsMethodArgumentResolver(reactiveRegistry)); |
|
|
|
} |
|
|
|
registrar.add(new ServerWebExchangeArgumentResolver(reactiveRegistry)); |
|
|
|
result.add(new ModelArgumentResolver(reactiveRegistry)); |
|
|
|
registrar.add(new PrincipalArgumentResolver(reactiveRegistry)); |
|
|
|
if (supportDataBinding) { |
|
|
|
registrar.addIfRequestBody(readers -> new SessionStatusMethodArgumentResolver()); |
|
|
|
result.add(new ErrorsMethodArgumentResolver(reactiveRegistry)); |
|
|
|
registrar.add(new WebSessionArgumentResolver(reactiveRegistry)); |
|
|
|
} |
|
|
|
|
|
|
|
result.add(new ServerWebExchangeArgumentResolver(reactiveRegistry)); |
|
|
|
|
|
|
|
result.add(new PrincipalArgumentResolver(reactiveRegistry)); |
|
|
|
|
|
|
|
if (requestMappingMethod) { |
|
|
|
|
|
|
|
result.add(new SessionStatusMethodArgumentResolver()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
result.add(new WebSessionArgumentResolver(reactiveRegistry)); |
|
|
|
|
|
|
|
|
|
|
|
// Custom...
|
|
|
|
// Custom...
|
|
|
|
registrar.addCustomResolvers(); |
|
|
|
result.addAll(customResolvers.getCustomResolvers()); |
|
|
|
|
|
|
|
|
|
|
|
// Catch-all...
|
|
|
|
// Catch-all...
|
|
|
|
registrar.add(new RequestParamMethodArgumentResolver(beanFactory, reactiveRegistry, true)); |
|
|
|
result.add(new RequestParamMethodArgumentResolver(beanFactory, reactiveRegistry, true)); |
|
|
|
registrar.addIfModelAttribute(() -> new ModelAttributeMethodArgumentResolver(reactiveRegistry, true)); |
|
|
|
if (supportDataBinding) { |
|
|
|
|
|
|
|
result.add(new ModelAttributeMethodArgumentResolver(reactiveRegistry, true)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return result; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void initControllerAdviceCaches(@Nullable ApplicationContext applicationContext) { |
|
|
|
private void initControllerAdviceCaches(ApplicationContext applicationContext) { |
|
|
|
if (applicationContext == null) { |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (logger.isInfoEnabled()) { |
|
|
|
if (logger.isInfoEnabled()) { |
|
|
|
logger.info("Looking for @ControllerAdvice: " + applicationContext); |
|
|
|
logger.info("Looking for @ControllerAdvice: " + applicationContext); |
|
|
|
} |
|
|
|
} |
|
|
|
@ -354,84 +382,4 @@ class ControllerMethodResolver { |
|
|
|
(AnnotationUtils.findAnnotation(method, RequestMapping.class) == null) && |
|
|
|
(AnnotationUtils.findAnnotation(method, RequestMapping.class) == null) && |
|
|
|
(AnnotationUtils.findAnnotation(method, ModelAttribute.class) != null); |
|
|
|
(AnnotationUtils.findAnnotation(method, ModelAttribute.class) != null); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static class ArgumentResolverRegistrar { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private final List<HandlerMethodArgumentResolver> customResolvers; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private final List<HttpMessageReader<?>> messageReaders; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private final boolean modelAttributeSupported; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private final List<HandlerMethodArgumentResolver> result = new ArrayList<>(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private ArgumentResolverRegistrar(ArgumentResolverConfigurer resolvers, |
|
|
|
|
|
|
|
List<HttpMessageReader<?>> messageReaders, boolean modelAttribute) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.customResolvers = resolvers.getCustomResolvers(); |
|
|
|
|
|
|
|
this.messageReaders = messageReaders; |
|
|
|
|
|
|
|
this.modelAttributeSupported = modelAttribute; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void add(HandlerMethodArgumentResolver resolver) { |
|
|
|
|
|
|
|
this.result.add(resolver); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void addIfRequestBody(Function<List<HttpMessageReader<?>>, HandlerMethodArgumentResolver> function) { |
|
|
|
|
|
|
|
if (!CollectionUtils.isEmpty(this.messageReaders)) { |
|
|
|
|
|
|
|
add(function.apply(this.messageReaders)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void addIfModelAttribute(Supplier<HandlerMethodArgumentResolver> supplier) { |
|
|
|
|
|
|
|
if (this.modelAttributeSupported) { |
|
|
|
|
|
|
|
add(supplier.get()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void addCustomResolvers() { |
|
|
|
|
|
|
|
this.customResolvers.forEach(this::add); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public List<HandlerMethodArgumentResolver> getResolvers() { |
|
|
|
|
|
|
|
return this.result; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public List<SyncHandlerMethodArgumentResolver> getSyncResolvers() { |
|
|
|
|
|
|
|
return this.result.stream() |
|
|
|
|
|
|
|
.filter(resolver -> resolver instanceof SyncHandlerMethodArgumentResolver) |
|
|
|
|
|
|
|
.map(resolver -> (SyncHandlerMethodArgumentResolver) resolver) |
|
|
|
|
|
|
|
.collect(Collectors.toList()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static Builder configurer(ArgumentResolverConfigurer configurer) { |
|
|
|
|
|
|
|
return new Builder(configurer); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static class Builder { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private final ArgumentResolverConfigurer resolvers; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public Builder(ArgumentResolverConfigurer configurer) { |
|
|
|
|
|
|
|
this.resolvers = configurer; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public ArgumentResolverRegistrar fullSupport(List<HttpMessageReader<?>> httpMessageReaders) { |
|
|
|
|
|
|
|
return new ArgumentResolverRegistrar(this.resolvers, httpMessageReaders, true); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public ArgumentResolverRegistrar modelAttributeSupport() { |
|
|
|
|
|
|
|
return new ArgumentResolverRegistrar(this.resolvers, Collections.emptyList(), true); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public ArgumentResolverRegistrar basic() { |
|
|
|
|
|
|
|
return new ArgumentResolverRegistrar(this.resolvers, Collections.emptyList(), false); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|