Polish
This commit is contained in:
parent
8f78c772b5
commit
bc470fca30
|
@ -243,13 +243,12 @@ public final class ModelFactory {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Derive the model attribute name for a method parameter based on:
|
* Derive the model attribute name for the given method parameter based on
|
||||||
* <ol>
|
* a {@code @ModelAttribute} parameter annotation (if present) or falling
|
||||||
* <li>the parameter {@code @ModelAttribute} annotation value
|
* back on parameter type based conventions.
|
||||||
* <li>the parameter type
|
|
||||||
* </ol>
|
|
||||||
* @param parameter a descriptor for the method parameter
|
* @param parameter a descriptor for the method parameter
|
||||||
* @return the derived name (never {@code null} or empty String)
|
* @return the derived name
|
||||||
|
* @see Conventions#getVariableNameForParameter(MethodParameter)
|
||||||
*/
|
*/
|
||||||
public static String getNameForParameter(MethodParameter parameter) {
|
public static String getNameForParameter(MethodParameter parameter) {
|
||||||
ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
|
ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
|
||||||
|
|
|
@ -74,10 +74,7 @@ public class SessionAttributesHandler {
|
||||||
this.attributeNames.addAll(Arrays.asList(annotation.names()));
|
this.attributeNames.addAll(Arrays.asList(annotation.names()));
|
||||||
this.attributeTypes.addAll(Arrays.asList(annotation.types()));
|
this.attributeTypes.addAll(Arrays.asList(annotation.types()));
|
||||||
}
|
}
|
||||||
|
this.knownAttributeNames.addAll(this.attributeNames);
|
||||||
for (String attributeName : this.attributeNames) {
|
|
||||||
this.knownAttributeNames.add(attributeName);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -90,7 +87,7 @@ public class SessionAttributesHandler {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the attribute name or type match the names and types specified
|
* Whether the attribute name or type match the names and types specified
|
||||||
* via {@code @SessionAttributes} in underlying controller.
|
* via {@code @SessionAttributes} on the underlying controller.
|
||||||
* <p>Attributes successfully resolved through this method are "remembered"
|
* <p>Attributes successfully resolved through this method are "remembered"
|
||||||
* and subsequently used in {@link #retrieveAttributes(WebRequest)} and
|
* and subsequently used in {@link #retrieveAttributes(WebRequest)} and
|
||||||
* {@link #cleanupAttributes(WebRequest)}.
|
* {@link #cleanupAttributes(WebRequest)}.
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2016 the original author or authors.
|
* Copyright 2002-2017 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.
|
||||||
|
@ -30,12 +30,15 @@ import org.springframework.web.bind.support.SessionAttributeStore;
|
||||||
import org.springframework.web.context.request.NativeWebRequest;
|
import org.springframework.web.context.request.NativeWebRequest;
|
||||||
import org.springframework.web.context.request.ServletWebRequest;
|
import org.springframework.web.context.request.ServletWebRequest;
|
||||||
|
|
||||||
import static java.util.Arrays.*;
|
import static java.util.Arrays.asList;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test fixture with {@link SessionAttributesHandler}.
|
* Test fixture with {@link SessionAttributesHandler}.
|
||||||
*
|
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
*/
|
*/
|
||||||
public class SessionAttributesHandlerTests {
|
public class SessionAttributesHandlerTests {
|
||||||
|
@ -50,10 +53,10 @@ public class SessionAttributesHandlerTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void isSessionAttribute() throws Exception {
|
public void isSessionAttribute() throws Exception {
|
||||||
assertTrue(sessionAttributesHandler.isHandlerSessionAttribute("attr1", null));
|
assertTrue(sessionAttributesHandler.isHandlerSessionAttribute("attr1", String.class));
|
||||||
assertTrue(sessionAttributesHandler.isHandlerSessionAttribute("attr2", null));
|
assertTrue(sessionAttributesHandler.isHandlerSessionAttribute("attr2", String.class));
|
||||||
assertTrue(sessionAttributesHandler.isHandlerSessionAttribute("simple", TestBean.class));
|
assertTrue(sessionAttributesHandler.isHandlerSessionAttribute("simple", TestBean.class));
|
||||||
assertFalse(sessionAttributesHandler.isHandlerSessionAttribute("simple", null));
|
assertFalse(sessionAttributesHandler.isHandlerSessionAttribute("simple", String.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -24,7 +24,8 @@ import org.springframework.web.bind.support.WebExchangeDataBinder;
|
||||||
import org.springframework.web.server.ServerWebExchange;
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Context to assist with processing a request and binding it onto Objects.
|
* Context to assist with binding request data onto Objects and provide access
|
||||||
|
* to a shared {@link Model} with controller-specific attributes.
|
||||||
*
|
*
|
||||||
* <p>Provides methods to create a {@link WebExchangeDataBinder} for a specific
|
* <p>Provides methods to create a {@link WebExchangeDataBinder} for a specific
|
||||||
* target, command Object to apply data binding and validation to, or without a
|
* target, command Object to apply data binding and validation to, or without a
|
||||||
|
|
|
@ -30,6 +30,7 @@ import org.springframework.core.ReactiveAdapter;
|
||||||
import org.springframework.core.ReactiveAdapterRegistry;
|
import org.springframework.core.ReactiveAdapterRegistry;
|
||||||
import org.springframework.core.ResolvableType;
|
import org.springframework.core.ResolvableType;
|
||||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
import org.springframework.web.reactive.BindingContext;
|
import org.springframework.web.reactive.BindingContext;
|
||||||
|
@ -73,12 +74,13 @@ class ModelInitializer {
|
||||||
List<Mono<HandlerResult>> resultList = new ArrayList<>();
|
List<Mono<HandlerResult>> resultList = new ArrayList<>();
|
||||||
attributeMethods.forEach(invocable -> resultList.add(invocable.invoke(exchange, bindingContext)));
|
attributeMethods.forEach(invocable -> resultList.add(invocable.invoke(exchange, bindingContext)));
|
||||||
|
|
||||||
return Mono.zip(resultList, objectArray -> {
|
return Mono
|
||||||
|
.zip(resultList, objectArray -> {
|
||||||
return Arrays.stream(objectArray)
|
return Arrays.stream(objectArray)
|
||||||
.map(object -> (HandlerResult) object)
|
.map(object -> handleResult(((HandlerResult) object), bindingContext))
|
||||||
.map(handlerResult -> handleResult(handlerResult, bindingContext))
|
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}).flatMap(completionList -> Mono.when(completionList));
|
})
|
||||||
|
.flatMap(completionList -> Mono.when(completionList));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Mono<Void> handleResult(HandlerResult handlerResult, BindingContext bindingContext) {
|
private Mono<Void> handleResult(HandlerResult handlerResult, BindingContext bindingContext) {
|
||||||
|
@ -86,18 +88,19 @@ class ModelInitializer {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
ResolvableType type = handlerResult.getReturnType();
|
ResolvableType type = handlerResult.getReturnType();
|
||||||
ReactiveAdapter adapter = this.adapterRegistry.getAdapter(type.getRawClass(), value);
|
ReactiveAdapter adapter = this.adapterRegistry.getAdapter(type.getRawClass(), value);
|
||||||
if (adapter != null) {
|
if (isAsyncVoidType(type, adapter)) {
|
||||||
Class<?> attributeType = (adapter.isNoValue() ? Void.class : type.resolveGeneric());
|
|
||||||
if (attributeType == Void.class) {
|
|
||||||
return Mono.from(adapter.toPublisher(value));
|
return Mono.from(adapter.toPublisher(value));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
String name = getAttributeName(handlerResult.getReturnTypeSource());
|
String name = getAttributeName(handlerResult.getReturnTypeSource());
|
||||||
bindingContext.getModel().asMap().putIfAbsent(name, value);
|
bindingContext.getModel().asMap().putIfAbsent(name, value);
|
||||||
}
|
}
|
||||||
return Mono.empty();
|
return Mono.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isAsyncVoidType(ResolvableType type, @Nullable ReactiveAdapter adapter) {
|
||||||
|
return adapter != null && (adapter.isNoValue() || type.resolveGeneric() == Void.class);
|
||||||
|
}
|
||||||
|
|
||||||
private String getAttributeName(MethodParameter param) {
|
private String getAttributeName(MethodParameter param) {
|
||||||
return Optional
|
return Optional
|
||||||
.ofNullable(AnnotatedElementUtils.findMergedAnnotation(param.getAnnotatedElement(), ModelAttribute.class))
|
.ofNullable(AnnotatedElementUtils.findMergedAnnotation(param.getAnnotatedElement(), ModelAttribute.class))
|
||||||
|
|
|
@ -147,7 +147,6 @@ public abstract class AbstractView implements View, ApplicationContextAware {
|
||||||
* Obtain the ApplicationContext for actual use.
|
* Obtain the ApplicationContext for actual use.
|
||||||
* @return the ApplicationContext (never {@code null})
|
* @return the ApplicationContext (never {@code null})
|
||||||
* @throws IllegalStateException in case of no ApplicationContext set
|
* @throws IllegalStateException in case of no ApplicationContext set
|
||||||
* @since 5.0
|
|
||||||
*/
|
*/
|
||||||
protected final ApplicationContext obtainApplicationContext() {
|
protected final ApplicationContext obtainApplicationContext() {
|
||||||
ApplicationContext applicationContext = getApplicationContext();
|
ApplicationContext applicationContext = getApplicationContext();
|
||||||
|
@ -191,7 +190,9 @@ public abstract class AbstractView implements View, ApplicationContextAware {
|
||||||
* <p>The default implementation creates a combined output Map that includes
|
* <p>The default implementation creates a combined output Map that includes
|
||||||
* model as well as static attributes with the former taking precedence.
|
* model as well as static attributes with the former taking precedence.
|
||||||
*/
|
*/
|
||||||
protected Mono<Map<String, Object>> getModelAttributes(@Nullable Map<String, ?> model, ServerWebExchange exchange) {
|
protected Mono<Map<String, Object>> getModelAttributes(@Nullable Map<String, ?> model,
|
||||||
|
ServerWebExchange exchange) {
|
||||||
|
|
||||||
int size = (model != null ? model.size() : 0);
|
int size = (model != null ? model.size() : 0);
|
||||||
|
|
||||||
Map<String, Object> attributes = new LinkedHashMap<>(size);
|
Map<String, Object> attributes = new LinkedHashMap<>(size);
|
||||||
|
@ -203,9 +204,11 @@ public abstract class AbstractView implements View, ApplicationContextAware {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* By default, resolve async attributes supported by the {@link ReactiveAdapterRegistry} to their blocking counterparts.
|
* By default, resolve async attributes supported by the
|
||||||
* <p>View implementations capable of taking advantage of reactive types can override this method if needed.
|
* {@link ReactiveAdapterRegistry} to their blocking counterparts.
|
||||||
* @return {@code Mono} to represent when the async attributes have been resolved
|
* <p>View implementations capable of taking advantage of reactive types
|
||||||
|
* can override this method if needed.
|
||||||
|
* @return {@code Mono} for the completion of async attributes resolution
|
||||||
*/
|
*/
|
||||||
protected Mono<Void> resolveAsyncAttributes(Map<String, Object> model) {
|
protected Mono<Void> resolveAsyncAttributes(Map<String, Object> model) {
|
||||||
|
|
||||||
|
@ -252,8 +255,9 @@ public abstract class AbstractView implements View, ApplicationContextAware {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a RequestContext to expose under the specified attribute name.
|
* Create a RequestContext to expose under the specified attribute name.
|
||||||
* <p>The default implementation creates a standard RequestContext instance for the
|
* <p>The default implementation creates a standard RequestContext instance
|
||||||
* given request and model. Can be overridden in subclasses for custom instances.
|
* for the given request and model. Can be overridden in subclasses for
|
||||||
|
* custom instances.
|
||||||
* @param exchange current exchange
|
* @param exchange current exchange
|
||||||
* @param model combined output Map (never {@code null}),
|
* @param model combined output Map (never {@code null}),
|
||||||
* with dynamic values taking precedence over static attributes
|
* with dynamic values taking precedence over static attributes
|
||||||
|
@ -269,7 +273,8 @@ public abstract class AbstractView implements View, ApplicationContextAware {
|
||||||
* <p>The default implementation looks in the {@link #getApplicationContext()
|
* <p>The default implementation looks in the {@link #getApplicationContext()
|
||||||
* Spring configuration} for a {@code RequestDataValueProcessor} bean with
|
* Spring configuration} for a {@code RequestDataValueProcessor} bean with
|
||||||
* the name {@link #REQUEST_DATA_VALUE_PROCESSOR_BEAN_NAME}.
|
* the name {@link #REQUEST_DATA_VALUE_PROCESSOR_BEAN_NAME}.
|
||||||
* @return the RequestDataValueProcessor, or null if there is none at the application context.
|
* @return the RequestDataValueProcessor, or null if there is none at the
|
||||||
|
* application context.
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
protected RequestDataValueProcessor getRequestDataValueProcessor() {
|
protected RequestDataValueProcessor getRequestDataValueProcessor() {
|
||||||
|
@ -286,7 +291,8 @@ public abstract class AbstractView implements View, ApplicationContextAware {
|
||||||
* with dynamic values taking precedence over static attributes
|
* with dynamic values taking precedence over static attributes
|
||||||
* @param contentType the content type selected to render with which should
|
* @param contentType the content type selected to render with which should
|
||||||
* match one of the {@link #getSupportedMediaTypes() supported media types}.
|
* match one of the {@link #getSupportedMediaTypes() supported media types}.
|
||||||
*@param exchange current exchange @return {@code Mono} to represent when and if rendering succeeds
|
*@param exchange current exchange @return {@code Mono} to represent when
|
||||||
|
* and if rendering succeeds
|
||||||
*/
|
*/
|
||||||
protected abstract Mono<Void> renderInternal(Map<String, Object> renderAttributes,
|
protected abstract Mono<Void> renderInternal(Map<String, Object> renderAttributes,
|
||||||
@Nullable MediaType contentType, ServerWebExchange exchange);
|
@Nullable MediaType contentType, ServerWebExchange exchange);
|
||||||
|
|
Loading…
Reference in New Issue