GenericTypeResolver's resolveTypeArguments needs to return null for raw types (for backwards compatibility with 3.2)
Issue: SPR-11052
This commit is contained in:
parent
84bc474016
commit
9f3b8a2430
|
|
@ -240,12 +240,12 @@ public abstract class GenericTypeResolver {
|
||||||
* @return the resolved type of each argument, with the array size matching the
|
* @return the resolved type of each argument, with the array size matching the
|
||||||
* number of actual type arguments, or {@code null} if not resolvable
|
* number of actual type arguments, or {@code null} if not resolvable
|
||||||
*/
|
*/
|
||||||
public static Class[] resolveTypeArguments(Class<?> clazz, Class<?> genericIfc) {
|
public static Class<?>[] resolveTypeArguments(Class<?> clazz, Class<?> genericIfc) {
|
||||||
ResolvableType type = ResolvableType.forClass(clazz).as(genericIfc);
|
ResolvableType type = ResolvableType.forClass(clazz).as(genericIfc);
|
||||||
if (!type.hasGenerics()) {
|
if (!type.hasGenerics() || type.hasUnresolvableGenerics()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return type.resolveGenerics(Object.class);
|
return type.resolveGenerics();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -567,7 +567,7 @@ public final class ResolvableType implements Serializable {
|
||||||
this.resolved = resolveClass();
|
this.resolved = resolveClass();
|
||||||
this.isResolved = true;
|
this.isResolved = true;
|
||||||
}
|
}
|
||||||
return (this.resolved == null ? fallback : this.resolved);
|
return (this.resolved != null ? this.resolved : fallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Class<?> resolveClass() {
|
private Class<?> resolveClass() {
|
||||||
|
|
@ -576,7 +576,7 @@ public final class ResolvableType implements Serializable {
|
||||||
}
|
}
|
||||||
if (this.type instanceof GenericArrayType) {
|
if (this.type instanceof GenericArrayType) {
|
||||||
Class<?> resolvedComponent = getComponentType().resolve();
|
Class<?> resolvedComponent = getComponentType().resolve();
|
||||||
return (resolvedComponent == null ? null : Array.newInstance(resolvedComponent, 0).getClass());
|
return (resolvedComponent != null ? Array.newInstance(resolvedComponent, 0).getClass() : null);
|
||||||
}
|
}
|
||||||
return resolveType().resolve();
|
return resolveType().resolve();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -138,10 +138,17 @@ public class GenericTypeResolverTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getGenericsCannotBeResovled() throws Exception {
|
public void getGenericsCannotBeResolved() throws Exception {
|
||||||
// SPR-11030
|
// SPR-11030
|
||||||
Class[] resolved = GenericTypeResolver.resolveTypeArguments(List.class, Iterable.class);
|
Class<?>[] resolved = GenericTypeResolver.resolveTypeArguments(List.class, Iterable.class);
|
||||||
assertThat(resolved, equalTo(new Class[] { Object.class }));
|
assertNull(resolved);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getRawMapTypeCannotBeResolved() throws Exception {
|
||||||
|
// SPR-11052
|
||||||
|
Class<?>[] resolved = GenericTypeResolver.resolveTypeArguments(Map.class, Map.class);
|
||||||
|
assertNull(resolved);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -300,11 +307,9 @@ public class GenericTypeResolverTests {
|
||||||
static abstract class WithArrayBase<T> {
|
static abstract class WithArrayBase<T> {
|
||||||
|
|
||||||
public abstract T[] array(T... args);
|
public abstract T[] array(T... args);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static abstract class WithArray<T> extends WithArrayBase<T> {
|
static abstract class WithArray<T> extends WithArrayBase<T> {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@
|
||||||
package org.springframework.web.socket.adapter;
|
package org.springframework.web.socket.adapter;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
import javax.websocket.DecodeException;
|
import javax.websocket.DecodeException;
|
||||||
import javax.websocket.Decoder;
|
import javax.websocket.Decoder;
|
||||||
import javax.websocket.EncodeException;
|
import javax.websocket.EncodeException;
|
||||||
|
|
@ -65,10 +64,8 @@ import org.springframework.web.context.ContextLoader;
|
||||||
*
|
*
|
||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
* @since 4.0
|
* @since 4.0
|
||||||
*
|
* @param <T> the type being converted to (for Encoder) or from (for Decoder)
|
||||||
* @param <T> The type being converted to (for Encoder) or from (for Decoder)
|
* @param <M> the WebSocket message type ({@link String} or {@link ByteBuffer})
|
||||||
* @param <M> The WebSocket message type ({@link String} or {@link ByteBuffer})
|
|
||||||
*
|
|
||||||
* @see ConvertingEncoderDecoderSupport.BinaryEncoder
|
* @see ConvertingEncoderDecoderSupport.BinaryEncoder
|
||||||
* @see ConvertingEncoderDecoderSupport.BinaryDecoder
|
* @see ConvertingEncoderDecoderSupport.BinaryDecoder
|
||||||
* @see ConvertingEncoderDecoderSupport.TextEncoder
|
* @see ConvertingEncoderDecoderSupport.TextEncoder
|
||||||
|
|
@ -107,15 +104,13 @@ public abstract class ConvertingEncoderDecoderSupport<T, M> {
|
||||||
*/
|
*/
|
||||||
protected ConversionService getConversionService() {
|
protected ConversionService getConversionService() {
|
||||||
ApplicationContext applicationContext = getApplicationContext();
|
ApplicationContext applicationContext = getApplicationContext();
|
||||||
Assert.state(applicationContext != null,
|
Assert.state(applicationContext != null, "Unable to locate the Spring ApplicationContext");
|
||||||
"Unable to locate the Spring ApplicationContext");
|
|
||||||
try {
|
try {
|
||||||
return applicationContext.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class);
|
return applicationContext.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class);
|
||||||
}
|
}
|
||||||
catch (BeansException ex) {
|
catch (BeansException ex) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException("Unable to find ConversionService: please configure a '" +
|
||||||
"Unable to find ConversionService, please configure a '"
|
CONVERSION_SERVICE_BEAN_NAME + "' or override the getConversionService() method", ex);
|
||||||
+ CONVERSION_SERVICE_BEAN_NAME + "' or override getConversionService()", ex);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -148,8 +143,12 @@ public abstract class ConvertingEncoderDecoderSupport<T, M> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Class<?>[] resolveTypeArguments() {
|
private Class<?>[] resolveTypeArguments() {
|
||||||
return GenericTypeResolver.resolveTypeArguments(getClass(),
|
Class<?>[] resolved = GenericTypeResolver.resolveTypeArguments(getClass(), ConvertingEncoderDecoderSupport.class);
|
||||||
ConvertingEncoderDecoderSupport.class);
|
if (resolved == null) {
|
||||||
|
throw new IllegalStateException("ConvertingEncoderDecoderSupport's generic types T and M " +
|
||||||
|
"need to be substituted in subclass: " + getClass());
|
||||||
|
}
|
||||||
|
return resolved;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -185,12 +184,12 @@ public abstract class ConvertingEncoderDecoderSupport<T, M> {
|
||||||
}
|
}
|
||||||
catch (ConversionException ex) {
|
catch (ConversionException ex) {
|
||||||
if (message instanceof String) {
|
if (message instanceof String) {
|
||||||
throw new DecodeException((String) message, "Unable to decode " +
|
throw new DecodeException((String) message,
|
||||||
"websocket message using ConversionService", ex);
|
"Unable to decode websocket message using ConversionService", ex);
|
||||||
}
|
}
|
||||||
if (message instanceof ByteBuffer) {
|
if (message instanceof ByteBuffer) {
|
||||||
throw new DecodeException((ByteBuffer) message, "Unable to decode " +
|
throw new DecodeException((ByteBuffer) message,
|
||||||
"websocket message using ConversionService", ex);
|
"Unable to decode websocket message using ConversionService", ex);
|
||||||
}
|
}
|
||||||
throw ex;
|
throw ex;
|
||||||
}
|
}
|
||||||
|
|
@ -198,50 +197,43 @@ public abstract class ConvertingEncoderDecoderSupport<T, M> {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Binary {@link javax.websocket.Encoder.Binary javax.websocket.Encoder} that
|
* A binary {@link javax.websocket.Encoder.Binary javax.websocket.Encoder} that delegates
|
||||||
* delegates to Spring's conversion service. See
|
* to Spring's conversion service. See {@link ConvertingEncoderDecoderSupport} for details.
|
||||||
* {@link ConvertingEncoderDecoderSupport} for details.
|
* @param <T> the type that this Encoder can convert to
|
||||||
*
|
|
||||||
* @param <T> The type that this Encoder can convert to.
|
|
||||||
*/
|
*/
|
||||||
public static abstract class BinaryEncoder<T> extends
|
public static abstract class BinaryEncoder<T> extends ConvertingEncoderDecoderSupport<T, ByteBuffer>
|
||||||
ConvertingEncoderDecoderSupport<T, ByteBuffer> implements Encoder.Binary<T> {
|
implements Encoder.Binary<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Binary {@link javax.websocket.Encoder.Binary javax.websocket.Encoder} that delegates
|
* A binary {@link javax.websocket.Encoder.Binary javax.websocket.Encoder} that delegates
|
||||||
|
* to Spring's conversion service. See {@link ConvertingEncoderDecoderSupport} for details.
|
||||||
|
* @param <T> the type that this Decoder can convert from
|
||||||
|
*/
|
||||||
|
public static abstract class BinaryDecoder<T> extends ConvertingEncoderDecoderSupport<T, ByteBuffer>
|
||||||
|
implements Decoder.Binary<T> {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A text {@link javax.websocket.Encoder.Text javax.websocket.Encoder} that delegates
|
||||||
* to Spring's conversion service. See {@link ConvertingEncoderDecoderSupport} for
|
* to Spring's conversion service. See {@link ConvertingEncoderDecoderSupport} for
|
||||||
* details.
|
* details.
|
||||||
*
|
* @param <T> the type that this Encoder can convert to
|
||||||
* @param <T> The type that this Decoder can convert from.
|
|
||||||
*/
|
*/
|
||||||
public static abstract class BinaryDecoder<T> extends
|
public static abstract class TextEncoder<T> extends ConvertingEncoderDecoderSupport<T, String>
|
||||||
ConvertingEncoderDecoderSupport<T, ByteBuffer> implements Decoder.Binary<T> {
|
implements Encoder.Text<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Text {@link javax.websocket.Encoder.Text javax.websocket.Encoder} that delegates
|
* A Text {@link javax.websocket.Encoder.Text javax.websocket.Encoder} that delegates
|
||||||
* to Spring's conversion service. See {@link ConvertingEncoderDecoderSupport} for
|
* to Spring's conversion service. See {@link ConvertingEncoderDecoderSupport} for details.
|
||||||
* details.
|
* @param <T> the type that this Decoder can convert from
|
||||||
*
|
|
||||||
* @param <T> The type that this Encoder can convert to.
|
|
||||||
*/
|
*/
|
||||||
public static abstract class TextEncoder<T> extends
|
public static abstract class TextDecoder<T> extends ConvertingEncoderDecoderSupport<T, String>
|
||||||
ConvertingEncoderDecoderSupport<T, String> implements Encoder.Text<T> {
|
implements Decoder.Text<T> {
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Text {@link javax.websocket.Encoder.Text javax.websocket.Encoder} that delegates
|
|
||||||
* to Spring's conversion service. See {@link ConvertingEncoderDecoderSupport} for
|
|
||||||
* details.
|
|
||||||
*
|
|
||||||
* @param <T> The type that this Decoder can convert from.
|
|
||||||
*/
|
|
||||||
public static abstract class TextDecoder<T> extends
|
|
||||||
ConvertingEncoderDecoderSupport<T, String> implements Decoder.Text<T> {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue