From 09aa59f9e79e19a2f09e66002c665b6a5a03ae20 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 24 Oct 2023 22:53:00 +0200 Subject: [PATCH 1/2] Avoid ResolvableType for simple assignability check in copyProperties Closes gh-27246 --- .../org/springframework/beans/BeanUtils.java | 50 +++++++++++-------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java b/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java index 62676e04cd0..3df583a48a6 100644 --- a/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java +++ b/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java @@ -23,6 +23,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.lang.reflect.Type; import java.net.URI; import java.net.URL; import java.time.temporal.Temporal; @@ -615,8 +616,8 @@ public abstract class BeanUtils { * @return a corresponding MethodParameter object */ public static MethodParameter getWriteMethodParameter(PropertyDescriptor pd) { - if (pd instanceof GenericTypeAwarePropertyDescriptor typeAwarePd) { - return new MethodParameter(typeAwarePd.getWriteMethodParameter()); + if (pd instanceof GenericTypeAwarePropertyDescriptor gpd) { + return new MethodParameter(gpd.getWriteMethodParameter()); } else { Method writeMethod = pd.getWriteMethod(); @@ -787,38 +788,28 @@ public abstract class BeanUtils { if (editable != null) { if (!editable.isInstance(target)) { throw new IllegalArgumentException("Target class [" + target.getClass().getName() + - "] not assignable to Editable class [" + editable.getName() + "]"); + "] not assignable to editable class [" + editable.getName() + "]"); } actualEditable = editable; } PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable); Set ignoredProps = (ignoreProperties != null ? new HashSet<>(Arrays.asList(ignoreProperties)) : null); + CachedIntrospectionResults sourceResults = (actualEditable != source.getClass() ? + CachedIntrospectionResults.forClass(source.getClass()) : null); for (PropertyDescriptor targetPd : targetPds) { Method writeMethod = targetPd.getWriteMethod(); if (writeMethod != null && (ignoredProps == null || !ignoredProps.contains(targetPd.getName()))) { - PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName()); + PropertyDescriptor sourcePd = (sourceResults != null ? + sourceResults.getPropertyDescriptor(targetPd.getName()) : targetPd); if (sourcePd != null) { Method readMethod = sourcePd.getReadMethod(); if (readMethod != null) { - ResolvableType sourceResolvableType = ResolvableType.forMethodReturnType(readMethod); - ResolvableType targetResolvableType = ResolvableType.forMethodParameter(writeMethod, 0); - - // Ignore generic types in assignable check if either ResolvableType has unresolvable generics. - boolean isAssignable = - (sourceResolvableType.hasUnresolvableGenerics() || targetResolvableType.hasUnresolvableGenerics() ? - ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType()) : - targetResolvableType.isAssignableFrom(sourceResolvableType)); - - if (isAssignable) { + if (isAssignable(writeMethod, readMethod)) { try { - if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) { - readMethod.setAccessible(true); - } + ReflectionUtils.makeAccessible(readMethod); Object value = readMethod.invoke(source); - if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) { - writeMethod.setAccessible(true); - } + ReflectionUtils.makeAccessible(writeMethod); writeMethod.invoke(target, value); } catch (Throwable ex) { @@ -832,6 +823,24 @@ public abstract class BeanUtils { } } + private static boolean isAssignable(Method writeMethod, Method readMethod) { + Type paramType = writeMethod.getGenericParameterTypes()[0]; + if (paramType instanceof Class clazz) { + return ClassUtils.isAssignable(clazz, readMethod.getReturnType()); + } + else if (paramType.equals(readMethod.getGenericReturnType())) { + return true; + } + else { + ResolvableType sourceType = ResolvableType.forMethodReturnType(readMethod); + ResolvableType targetType = ResolvableType.forMethodParameter(writeMethod, 0); + // Ignore generic types in assignable check if either ResolvableType has unresolvable generics. + return (sourceType.hasUnresolvableGenerics() || targetType.hasUnresolvableGenerics() ? + ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType()) : + targetType.isAssignableFrom(sourceType)); + } + } + /** * Inner class to avoid a hard dependency on Kotlin at runtime. @@ -896,7 +905,6 @@ public abstract class BeanUtils { } return kotlinConstructor.callBy(argParameters); } - } } From 925fa0272b76c765d3f9dee2bd5d54c66bd849c9 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 24 Oct 2023 22:53:44 +0200 Subject: [PATCH 2/2] Polishing --- .../AbstractNestablePropertyAccessor.java | 4 +- .../beans/DirectFieldAccessor.java | 3 +- ...AbstractApplicationEventListenerTests.java | 24 ++- ...enericApplicationListenerAdapterTests.java | 3 +- .../springframework/core/ResolvableType.java | 177 +++++++++--------- .../core/ResolvableTypeTests.java | 51 +++-- 6 files changed, 135 insertions(+), 127 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java b/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java index c6eab5fa004..bee8f5e95ba 100644 --- a/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java @@ -1000,18 +1000,20 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA */ protected abstract static class PropertyHandler { + @Nullable private final Class propertyType; private final boolean readable; private final boolean writable; - public PropertyHandler(Class propertyType, boolean readable, boolean writable) { + public PropertyHandler(@Nullable Class propertyType, boolean readable, boolean writable) { this.propertyType = propertyType; this.readable = readable; this.writable = writable; } + @Nullable public Class getPropertyType() { return this.propertyType; } diff --git a/spring-beans/src/main/java/org/springframework/beans/DirectFieldAccessor.java b/spring-beans/src/main/java/org/springframework/beans/DirectFieldAccessor.java index a98c6eb41b0..0a9b39e2e47 100644 --- a/spring-beans/src/main/java/org/springframework/beans/DirectFieldAccessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/DirectFieldAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2023 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. @@ -129,7 +129,6 @@ public class DirectFieldAccessor extends AbstractNestablePropertyAccessor { ReflectionUtils.makeAccessible(this.field); return this.field.get(getWrappedInstance()); } - catch (IllegalAccessException ex) { throw new InvalidPropertyException(getWrappedClass(), this.field.getName(), "Field is not accessible", ex); diff --git a/spring-context/src/test/java/org/springframework/context/event/AbstractApplicationEventListenerTests.java b/spring-context/src/test/java/org/springframework/context/event/AbstractApplicationEventListenerTests.java index bf4360ef589..22d33a109cb 100644 --- a/spring-context/src/test/java/org/springframework/context/event/AbstractApplicationEventListenerTests.java +++ b/spring-context/src/test/java/org/springframework/context/event/AbstractApplicationEventListenerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2023 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. @@ -36,6 +36,10 @@ public abstract class AbstractApplicationEventListenerTests { } } + protected GenericTestEvent createGenericTestEvent(T payload) { + return new GenericTestEvent<>(this, payload); + } + protected static class GenericTestEvent extends ApplicationEvent { @@ -51,6 +55,7 @@ public abstract class AbstractApplicationEventListenerTests { } } + protected static class SmartGenericTestEvent extends GenericTestEvent implements ResolvableTypeProvider { private final ResolvableType resolvableType; @@ -67,6 +72,7 @@ public abstract class AbstractApplicationEventListenerTests { } } + protected static class StringEvent extends GenericTestEvent { public StringEvent(Object source, String payload) { @@ -74,6 +80,7 @@ public abstract class AbstractApplicationEventListenerTests { } } + protected static class LongEvent extends GenericTestEvent { public LongEvent(Object source, Long payload) { @@ -81,31 +88,31 @@ public abstract class AbstractApplicationEventListenerTests { } } - protected GenericTestEvent createGenericTestEvent(T payload) { - return new GenericTestEvent<>(this, payload); - } - static class GenericEventListener implements ApplicationListener> { + @Override public void onApplicationEvent(GenericTestEvent event) { } } + static class ObjectEventListener implements ApplicationListener> { + @Override public void onApplicationEvent(GenericTestEvent event) { } } - static class UpperBoundEventListener - implements ApplicationListener> { + + static class UpperBoundEventListener implements ApplicationListener> { @Override public void onApplicationEvent(GenericTestEvent event) { } } + static class StringEventListener implements ApplicationListener> { @Override @@ -113,6 +120,7 @@ public abstract class AbstractApplicationEventListenerTests { } } + @SuppressWarnings("rawtypes") static class RawApplicationListener implements ApplicationListener { @@ -121,10 +129,10 @@ public abstract class AbstractApplicationEventListenerTests { } } + static class TestEvents { public GenericTestEvent wildcardEvent; - } } diff --git a/spring-context/src/test/java/org/springframework/context/event/GenericApplicationListenerAdapterTests.java b/spring-context/src/test/java/org/springframework/context/event/GenericApplicationListenerAdapterTests.java index ceae6db126b..6fdb68bb0af 100644 --- a/spring-context/src/test/java/org/springframework/context/event/GenericApplicationListenerAdapterTests.java +++ b/spring-context/src/test/java/org/springframework/context/event/GenericApplicationListenerAdapterTests.java @@ -104,7 +104,8 @@ public class GenericApplicationListenerAdapterTests extends AbstractApplicationE @Test public void genericListenerStrictTypeSubClass() { - supportsEventType(false, ObjectEventListener.class, ResolvableType.forClassWithGenerics(GenericTestEvent.class, Long.class)); + supportsEventType(false, ObjectEventListener.class, + ResolvableType.forClassWithGenerics(GenericTestEvent.class, Long.class)); } @Test diff --git a/spring-core/src/main/java/org/springframework/core/ResolvableType.java b/spring-core/src/main/java/org/springframework/core/ResolvableType.java index 4746299e1fa..c7403097d30 100644 --- a/spring-core/src/main/java/org/springframework/core/ResolvableType.java +++ b/spring-core/src/main/java/org/springframework/core/ResolvableType.java @@ -138,7 +138,7 @@ public class ResolvableType implements Serializable { /** - * Private constructor used to create a new {@link ResolvableType} for cache key purposes, + * Private constructor used to create a new {@code ResolvableType} for cache key purposes, * with no upfront resolution. */ private ResolvableType( @@ -153,7 +153,7 @@ public class ResolvableType implements Serializable { } /** - * Private constructor used to create a new {@link ResolvableType} for cache value purposes, + * Private constructor used to create a new {@code ResolvableType} for cache value purposes, * with upfront resolution and a pre-calculated hash. * @since 4.2 */ @@ -169,7 +169,7 @@ public class ResolvableType implements Serializable { } /** - * Private constructor used to create a new {@link ResolvableType} for uncached purposes, + * Private constructor used to create a new {@code ResolvableType} for uncached purposes, * with upfront resolution but lazily calculated hash. */ private ResolvableType(Type type, @Nullable TypeProvider typeProvider, @@ -184,7 +184,7 @@ public class ResolvableType implements Serializable { } /** - * Private constructor used to create a new {@link ResolvableType} on a {@link Class} basis. + * Private constructor used to create a new {@code ResolvableType} on a {@link Class} basis. *

Avoids all {@code instanceof} checks in order to create a straight {@link Class} wrapper. * @since 4.2 */ @@ -223,7 +223,7 @@ public class ResolvableType implements Serializable { /** * Return the underlying source of the resolvable type. Will return a {@link Field}, - * {@link MethodParameter} or {@link Type} depending on how the {@link ResolvableType} + * {@link MethodParameter} or {@link Type} depending on how the {@code ResolvableType} * was constructed. This method is primarily to provide access to additional type * information or meta-data that alternative JVM languages may provide. */ @@ -340,13 +340,14 @@ public class ResolvableType implements Serializable { } } if (ourResolved == null) { - ourResolved = resolve(Object.class); + ourResolved = toClass(); } Class otherResolved = other.toClass(); // We need an exact type match for generics // List is not assignable from List - if (exactMatch ? !ourResolved.equals(otherResolved) : !ClassUtils.isAssignable(ourResolved, otherResolved)) { + if (exactMatch ? !ourResolved.equals(otherResolved) : + !ClassUtils.isAssignable(ourResolved, otherResolved)) { return false; } @@ -357,13 +358,15 @@ public class ResolvableType implements Serializable { if (ourGenerics.length != typeGenerics.length) { return false; } - if (matchedBefore == null) { - matchedBefore = new IdentityHashMap<>(1); - } - matchedBefore.put(this.type, other.type); - for (int i = 0; i < ourGenerics.length; i++) { - if (!ourGenerics[i].isAssignableFrom(typeGenerics[i], matchedBefore)) { - return false; + if (ourGenerics.length > 0) { + if (matchedBefore == null) { + matchedBefore = new IdentityHashMap<>(1); + } + matchedBefore.put(this.type, other.type); + for (int i = 0; i < ourGenerics.length; i++) { + if (!ourGenerics[i].isAssignableFrom(typeGenerics[i], matchedBefore)) { + return false; + } } } } @@ -428,12 +431,12 @@ public class ResolvableType implements Serializable { } /** - * Return this type as a {@link ResolvableType} of the specified class. Searches + * Return this type as a {@code ResolvableType} of the specified class. Searches * {@link #getSuperType() supertype} and {@link #getInterfaces() interface} * hierarchies to find a match, returning {@link #NONE} if this type does not * implement or extend the specified class. * @param type the required type (typically narrowed) - * @return a {@link ResolvableType} representing this object as the specified + * @return a {@code ResolvableType} representing this object as the specified * type, or {@link #NONE} if not resolvable as that type * @see #asCollection() * @see #asMap() @@ -458,9 +461,9 @@ public class ResolvableType implements Serializable { } /** - * Return a {@link ResolvableType} representing the direct supertype of this type. + * Return a {@code ResolvableType} representing the direct supertype of this type. *

If no supertype is available this method returns {@link #NONE}. - *

Note: The resulting {@link ResolvableType} instance may not be {@link Serializable}. + *

Note: The resulting {@code ResolvableType} instance may not be {@link Serializable}. * @see #getInterfaces() */ public ResolvableType getSuperType() { @@ -487,10 +490,10 @@ public class ResolvableType implements Serializable { } /** - * Return a {@link ResolvableType} array representing the direct interfaces + * Return a {@code ResolvableType} array representing the direct interfaces * implemented by this type. If this type does not implement any interfaces an * empty array is returned. - *

Note: The resulting {@link ResolvableType} instances may not be {@link Serializable}. + *

Note: The resulting {@code ResolvableType} instances may not be {@link Serializable}. * @see #getSuperType() */ public ResolvableType[] getInterfaces() { @@ -618,17 +621,17 @@ public class ResolvableType implements Serializable { } /** - * Return a {@link ResolvableType} for the specified nesting level. + * Return a {@code ResolvableType} for the specified nesting level. *

See {@link #getNested(int, Map)} for details. * @param nestingLevel the nesting level - * @return the {@link ResolvableType} type, or {@code #NONE} + * @return the {@code ResolvableType} type, or {@code #NONE} */ public ResolvableType getNested(int nestingLevel) { return getNested(nestingLevel, null); } /** - * Return a {@link ResolvableType} for the specified nesting level. + * Return a {@code ResolvableType} for the specified nesting level. *

The nesting level refers to the specific generic parameter that should be returned. * A nesting level of 1 indicates this type; 2 indicates the first nested generic; * 3 the second; and so on. For example, given {@code List>} level 1 refers @@ -645,7 +648,7 @@ public class ResolvableType implements Serializable { * current type, 2 for the first nested generic, 3 for the second and so on * @param typeIndexesPerLevel a map containing the generic index for a given * nesting level (may be {@code null}) - * @return a {@link ResolvableType} for the nested level, or {@link #NONE} + * @return a {@code ResolvableType} for the nested level, or {@link #NONE} */ public ResolvableType getNested(int nestingLevel, @Nullable Map typeIndexesPerLevel) { ResolvableType result = this; @@ -667,7 +670,7 @@ public class ResolvableType implements Serializable { } /** - * Return a {@link ResolvableType} representing the generic parameter for the + * Return a {@code ResolvableType} representing the generic parameter for the * given indexes. Indexes are zero based; for example given the type * {@code Map>}, {@code getGeneric(0)} will access the * {@code Integer}. Nested generics can be accessed by specifying multiple indexes; @@ -677,7 +680,7 @@ public class ResolvableType implements Serializable { *

If no generic is available at the specified indexes {@link #NONE} is returned. * @param indexes the indexes that refer to the generic parameter * (may be omitted to return the first generic) - * @return a {@link ResolvableType} for the specified generic, or {@link #NONE} + * @return a {@code ResolvableType} for the specified generic, or {@link #NONE} * @see #hasGenerics() * @see #getGenerics() * @see #resolveGeneric(int...) @@ -700,12 +703,12 @@ public class ResolvableType implements Serializable { } /** - * Return an array of {@link ResolvableType ResolvableTypes} representing the generic parameters of + * Return an array of {@code ResolvableType ResolvableTypes} representing the generic parameters of * this type. If no generics are available an empty array is returned. If you need to * access a specific generic consider using the {@link #getGeneric(int...)} method as * it allows access to nested generics and protects against * {@code IndexOutOfBoundsExceptions}. - * @return an array of {@link ResolvableType ResolvableTypes} representing the generic parameters + * @return an array of {@code ResolvableType ResolvableTypes} representing the generic parameters * (never {@code null}) * @see #hasGenerics() * @see #getGeneric(int...) @@ -839,7 +842,7 @@ public class ResolvableType implements Serializable { /** * Resolve this type by a single level, returning the resolved value or {@link #NONE}. - *

Note: The returned {@link ResolvableType} should only be used as an intermediary + *

Note: The returned {@code ResolvableType} should only be used as an intermediary * as it cannot be serialized. */ ResolvableType resolveType() { @@ -958,7 +961,7 @@ public class ResolvableType implements Serializable { } /** - * Adapts this {@link ResolvableType} to a {@link VariableResolver}. + * Adapts this {@code ResolvableType} to a {@link VariableResolver}. */ @Nullable VariableResolver asVariableResolver() { @@ -1004,12 +1007,12 @@ public class ResolvableType implements Serializable { // Factory methods /** - * Return a {@link ResolvableType} for the specified {@link Class}, + * Return a {@code ResolvableType} for the specified {@link Class}, * using the full generic type information for assignability checks. *

For example: {@code ResolvableType.forClass(MyArrayList.class)}. * @param clazz the class to introspect ({@code null} is semantically * equivalent to {@code Object.class} for typical use cases here) - * @return a {@link ResolvableType} for the specified class + * @return a {@code ResolvableType} for the specified class * @see #forClass(Class, Class) * @see #forClassWithGenerics(Class, Class...) */ @@ -1018,13 +1021,13 @@ public class ResolvableType implements Serializable { } /** - * Return a {@link ResolvableType} for the specified {@link Class}, + * Return a {@code ResolvableType} for the specified {@link Class}, * doing assignability checks against the raw class only (analogous to * {@link Class#isAssignableFrom}, which this serves as a wrapper for). *

For example: {@code ResolvableType.forRawClass(List.class)}. * @param clazz the class to introspect ({@code null} is semantically * equivalent to {@code Object.class} for typical use cases here) - * @return a {@link ResolvableType} for the specified class + * @return a {@code ResolvableType} for the specified class * @since 4.2 * @see #forClass(Class) * @see #getRawClass() @@ -1048,12 +1051,12 @@ public class ResolvableType implements Serializable { } /** - * Return a {@link ResolvableType} for the specified base type + * Return a {@code ResolvableType} for the specified base type * (interface or base class) with a given implementation class. *

For example: {@code ResolvableType.forClass(List.class, MyArrayList.class)}. * @param baseType the base type (must not be {@code null}) * @param implementationClass the implementation class - * @return a {@link ResolvableType} for the specified base type backed by the + * @return a {@code ResolvableType} for the specified base type backed by the * given implementation class * @see #forClass(Class) * @see #forClassWithGenerics(Class, Class...) @@ -1065,10 +1068,10 @@ public class ResolvableType implements Serializable { } /** - * Return a {@link ResolvableType} for the specified {@link Class} with pre-declared generics. + * Return a {@code ResolvableType} for the specified {@link Class} with pre-declared generics. * @param clazz the class (or interface) to introspect * @param generics the generics of the class - * @return a {@link ResolvableType} for the specific class and generics + * @return a {@code ResolvableType} for the specific class and generics * @see #forClassWithGenerics(Class, ResolvableType...) */ public static ResolvableType forClassWithGenerics(Class clazz, Class... generics) { @@ -1082,10 +1085,10 @@ public class ResolvableType implements Serializable { } /** - * Return a {@link ResolvableType} for the specified {@link Class} with pre-declared generics. + * Return a {@code ResolvableType} for the specified {@link Class} with pre-declared generics. * @param clazz the class (or interface) to introspect * @param generics the generics of the class - * @return a {@link ResolvableType} for the specific class and generics + * @return a {@code ResolvableType} for the specific class and generics * @see #forClassWithGenerics(Class, Class...) */ public static ResolvableType forClassWithGenerics(Class clazz, ResolvableType... generics) { @@ -1106,12 +1109,12 @@ public class ResolvableType implements Serializable { } /** - * Return a {@link ResolvableType} for the specified instance. The instance does not + * Return a {@code ResolvableType} for the specified instance. The instance does not * convey generic information but if it implements {@link ResolvableTypeProvider} a - * more precise {@link ResolvableType} can be used than the simple one based on + * more precise {@code ResolvableType} can be used than the simple one based on * the {@link #forClass(Class) Class instance}. * @param instance the instance (possibly {@code null}) - * @return a {@link ResolvableType} for the specified instance, + * @return a {@code ResolvableType} for the specified instance, * or {@code NONE} for {@code null} * @since 4.2 * @see ResolvableTypeProvider @@ -1127,9 +1130,9 @@ public class ResolvableType implements Serializable { } /** - * Return a {@link ResolvableType} for the specified {@link Field}. + * Return a {@code ResolvableType} for the specified {@link Field}. * @param field the source field - * @return a {@link ResolvableType} for the specified field + * @return a {@code ResolvableType} for the specified field * @see #forField(Field, Class) */ public static ResolvableType forField(Field field) { @@ -1138,13 +1141,13 @@ public class ResolvableType implements Serializable { } /** - * Return a {@link ResolvableType} for the specified {@link Field} with a given + * Return a {@code ResolvableType} for the specified {@link Field} with a given * implementation. *

Use this variant when the class that declares the field includes generic * parameter variables that are satisfied by the implementation class. * @param field the source field * @param implementationClass the implementation class - * @return a {@link ResolvableType} for the specified field + * @return a {@code ResolvableType} for the specified field * @see #forField(Field) */ public static ResolvableType forField(Field field, Class implementationClass) { @@ -1154,13 +1157,13 @@ public class ResolvableType implements Serializable { } /** - * Return a {@link ResolvableType} for the specified {@link Field} with a given + * Return a {@code ResolvableType} for the specified {@link Field} with a given * implementation. *

Use this variant when the class that declares the field includes generic * parameter variables that are satisfied by the implementation type. * @param field the source field * @param implementationType the implementation type - * @return a {@link ResolvableType} for the specified field + * @return a {@code ResolvableType} for the specified field * @see #forField(Field) */ public static ResolvableType forField(Field field, @Nullable ResolvableType implementationType) { @@ -1171,7 +1174,7 @@ public class ResolvableType implements Serializable { } /** - * Return a {@link ResolvableType} for the specified {@link Field} with the + * Return a {@code ResolvableType} for the specified {@link Field} with the * given nesting level. * @param field the source field * @param nestingLevel the nesting level (1 for the outer level; 2 for a nested @@ -1184,7 +1187,7 @@ public class ResolvableType implements Serializable { } /** - * Return a {@link ResolvableType} for the specified {@link Field} with a given + * Return a {@code ResolvableType} for the specified {@link Field} with a given * implementation and the given nesting level. *

Use this variant when the class that declares the field includes generic * parameter variables that are satisfied by the implementation class. @@ -1192,7 +1195,7 @@ public class ResolvableType implements Serializable { * @param nestingLevel the nesting level (1 for the outer level; 2 for a nested * generic type; etc) * @param implementationClass the implementation class - * @return a {@link ResolvableType} for the specified field + * @return a {@code ResolvableType} for the specified field * @see #forField(Field) */ public static ResolvableType forField(Field field, int nestingLevel, @Nullable Class implementationClass) { @@ -1202,10 +1205,10 @@ public class ResolvableType implements Serializable { } /** - * Return a {@link ResolvableType} for the specified {@link Constructor} parameter. + * Return a {@code ResolvableType} for the specified {@link Constructor} parameter. * @param constructor the source constructor (must not be {@code null}) * @param parameterIndex the parameter index - * @return a {@link ResolvableType} for the specified constructor parameter + * @return a {@code ResolvableType} for the specified constructor parameter * @see #forConstructorParameter(Constructor, int, Class) */ public static ResolvableType forConstructorParameter(Constructor constructor, int parameterIndex) { @@ -1214,14 +1217,14 @@ public class ResolvableType implements Serializable { } /** - * Return a {@link ResolvableType} for the specified {@link Constructor} parameter + * Return a {@code ResolvableType} for the specified {@link Constructor} parameter * with a given implementation. Use this variant when the class that declares the * constructor includes generic parameter variables that are satisfied by the * implementation class. * @param constructor the source constructor (must not be {@code null}) * @param parameterIndex the parameter index * @param implementationClass the implementation class - * @return a {@link ResolvableType} for the specified constructor parameter + * @return a {@code ResolvableType} for the specified constructor parameter * @see #forConstructorParameter(Constructor, int) */ public static ResolvableType forConstructorParameter(Constructor constructor, int parameterIndex, @@ -1233,9 +1236,9 @@ public class ResolvableType implements Serializable { } /** - * Return a {@link ResolvableType} for the specified {@link Method} return type. + * Return a {@code ResolvableType} for the specified {@link Method} return type. * @param method the source for the method return type - * @return a {@link ResolvableType} for the specified method return + * @return a {@code ResolvableType} for the specified method return * @see #forMethodReturnType(Method, Class) */ public static ResolvableType forMethodReturnType(Method method) { @@ -1244,12 +1247,12 @@ public class ResolvableType implements Serializable { } /** - * Return a {@link ResolvableType} for the specified {@link Method} return type. + * Return a {@code ResolvableType} for the specified {@link Method} return type. *

Use this variant when the class that declares the method includes generic * parameter variables that are satisfied by the implementation class. * @param method the source for the method return type * @param implementationClass the implementation class - * @return a {@link ResolvableType} for the specified method return + * @return a {@code ResolvableType} for the specified method return * @see #forMethodReturnType(Method) */ public static ResolvableType forMethodReturnType(Method method, Class implementationClass) { @@ -1259,10 +1262,10 @@ public class ResolvableType implements Serializable { } /** - * Return a {@link ResolvableType} for the specified {@link Method} parameter. + * Return a {@code ResolvableType} for the specified {@link Method} parameter. * @param method the source method (must not be {@code null}) * @param parameterIndex the parameter index - * @return a {@link ResolvableType} for the specified method parameter + * @return a {@code ResolvableType} for the specified method parameter * @see #forMethodParameter(Method, int, Class) * @see #forMethodParameter(MethodParameter) */ @@ -1272,13 +1275,13 @@ public class ResolvableType implements Serializable { } /** - * Return a {@link ResolvableType} for the specified {@link Method} parameter with a + * Return a {@code ResolvableType} for the specified {@link Method} parameter with a * given implementation. Use this variant when the class that declares the method * includes generic parameter variables that are satisfied by the implementation class. * @param method the source method (must not be {@code null}) * @param parameterIndex the parameter index * @param implementationClass the implementation class - * @return a {@link ResolvableType} for the specified method parameter + * @return a {@code ResolvableType} for the specified method parameter * @see #forMethodParameter(Method, int, Class) * @see #forMethodParameter(MethodParameter) */ @@ -1289,9 +1292,9 @@ public class ResolvableType implements Serializable { } /** - * Return a {@link ResolvableType} for the specified {@link MethodParameter}. + * Return a {@code ResolvableType} for the specified {@link MethodParameter}. * @param methodParameter the source method parameter (must not be {@code null}) - * @return a {@link ResolvableType} for the specified method parameter + * @return a {@code ResolvableType} for the specified method parameter * @see #forMethodParameter(Method, int) */ public static ResolvableType forMethodParameter(MethodParameter methodParameter) { @@ -1299,12 +1302,12 @@ public class ResolvableType implements Serializable { } /** - * Return a {@link ResolvableType} for the specified {@link MethodParameter} with a + * Return a {@code ResolvableType} for the specified {@link MethodParameter} with a * given implementation type. Use this variant when the class that declares the method * includes generic parameter variables that are satisfied by the implementation type. * @param methodParameter the source method parameter (must not be {@code null}) * @param implementationType the implementation type - * @return a {@link ResolvableType} for the specified method parameter + * @return a {@code ResolvableType} for the specified method parameter * @see #forMethodParameter(MethodParameter) */ public static ResolvableType forMethodParameter(MethodParameter methodParameter, @@ -1319,11 +1322,11 @@ public class ResolvableType implements Serializable { } /** - * Return a {@link ResolvableType} for the specified {@link MethodParameter}, + * Return a {@code ResolvableType} for the specified {@link MethodParameter}, * overriding the target type to resolve with a specific given type. * @param methodParameter the source method parameter (must not be {@code null}) * @param targetType the type to resolve (a part of the method parameter's type) - * @return a {@link ResolvableType} for the specified method parameter + * @return a {@code ResolvableType} for the specified method parameter * @see #forMethodParameter(Method, int) */ public static ResolvableType forMethodParameter(MethodParameter methodParameter, @Nullable Type targetType) { @@ -1332,13 +1335,13 @@ public class ResolvableType implements Serializable { } /** - * Return a {@link ResolvableType} for the specified {@link MethodParameter} at + * Return a {@code ResolvableType} for the specified {@link MethodParameter} at * a specific nesting level, overriding the target type to resolve with a specific * given type. * @param methodParameter the source method parameter (must not be {@code null}) * @param targetType the type to resolve (a part of the method parameter's type) * @param nestingLevel the nesting level to use - * @return a {@link ResolvableType} for the specified method parameter + * @return a {@code ResolvableType} for the specified method parameter * @since 5.2 * @see #forMethodParameter(Method, int) */ @@ -1351,9 +1354,9 @@ public class ResolvableType implements Serializable { } /** - * Return a {@link ResolvableType} as an array of the specified {@code componentType}. + * Return a {@code ResolvableType} as an array of the specified {@code componentType}. * @param componentType the component type - * @return a {@link ResolvableType} as an array of the specified component type + * @return a {@code ResolvableType} as an array of the specified component type */ public static ResolvableType forArrayComponent(ResolvableType componentType) { Assert.notNull(componentType, "Component type must not be null"); @@ -1362,10 +1365,10 @@ public class ResolvableType implements Serializable { } /** - * Return a {@link ResolvableType} for the specified {@link Type}. - *

Note: The resulting {@link ResolvableType} instance may not be {@link Serializable}. + * Return a {@code ResolvableType} for the specified {@link Type}. + *

Note: The resulting {@code ResolvableType} instance may not be {@link Serializable}. * @param type the source type (potentially {@code null}) - * @return a {@link ResolvableType} for the specified {@link Type} + * @return a {@code ResolvableType} for the specified {@link Type} * @see #forType(Type, ResolvableType) */ public static ResolvableType forType(@Nullable Type type) { @@ -1373,12 +1376,12 @@ public class ResolvableType implements Serializable { } /** - * Return a {@link ResolvableType} for the specified {@link Type} backed by the given + * Return a {@code ResolvableType} for the specified {@link Type} backed by the given * owner type. - *

Note: The resulting {@link ResolvableType} instance may not be {@link Serializable}. + *

Note: The resulting {@code ResolvableType} instance may not be {@link Serializable}. * @param type the source type or {@code null} * @param owner the owner type used to resolve variables - * @return a {@link ResolvableType} for the specified {@link Type} and owner + * @return a {@code ResolvableType} for the specified {@link Type} and owner * @see #forType(Type) */ public static ResolvableType forType(@Nullable Type type, @Nullable ResolvableType owner) { @@ -1391,10 +1394,10 @@ public class ResolvableType implements Serializable { /** - * Return a {@link ResolvableType} for the specified {@link ParameterizedTypeReference}. - *

Note: The resulting {@link ResolvableType} instance may not be {@link Serializable}. + * Return a {@code ResolvableType} for the specified {@link ParameterizedTypeReference}. + *

Note: The resulting {@code ResolvableType} instance may not be {@link Serializable}. * @param typeReference the reference to obtain the source type from - * @return a {@link ResolvableType} for the specified {@link ParameterizedTypeReference} + * @return a {@code ResolvableType} for the specified {@link ParameterizedTypeReference} * @since 4.3.12 * @see #forType(Type) */ @@ -1403,23 +1406,23 @@ public class ResolvableType implements Serializable { } /** - * Return a {@link ResolvableType} for the specified {@link Type} backed by a given + * Return a {@code ResolvableType} for the specified {@link Type} backed by a given * {@link VariableResolver}. * @param type the source type or {@code null} * @param variableResolver the variable resolver or {@code null} - * @return a {@link ResolvableType} for the specified {@link Type} and {@link VariableResolver} + * @return a {@code ResolvableType} for the specified {@link Type} and {@link VariableResolver} */ static ResolvableType forType(@Nullable Type type, @Nullable VariableResolver variableResolver) { return forType(type, null, variableResolver); } /** - * Return a {@link ResolvableType} for the specified {@link Type} backed by a given + * Return a {@code ResolvableType} for the specified {@link Type} backed by a given * {@link VariableResolver}. * @param type the source type or {@code null} * @param typeProvider the type provider or {@code null} * @param variableResolver the variable resolver or {@code null} - * @return a {@link ResolvableType} for the specified {@link Type} and {@link VariableResolver} + * @return a {@code ResolvableType} for the specified {@link Type} and {@link VariableResolver} */ static ResolvableType forType( @Nullable Type type, @Nullable TypeProvider typeProvider, @Nullable VariableResolver variableResolver) { diff --git a/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java b/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java index db7bb456767..f4810cb29da 100644 --- a/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java +++ b/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java @@ -200,8 +200,8 @@ class ResolvableTypeTests { @Test void forFieldMustNotBeNull() throws Exception { assertThatIllegalArgumentException() - .isThrownBy(() -> ResolvableType.forField(null)) - .withMessage("Field must not be null"); + .isThrownBy(() -> ResolvableType.forField(null)) + .withMessage("Field must not be null"); } @Test @@ -214,8 +214,8 @@ class ResolvableTypeTests { @Test void forConstructorParameterMustNotBeNull() throws Exception { assertThatIllegalArgumentException() - .isThrownBy(() -> ResolvableType.forConstructorParameter(null, 0)) - .withMessage("Constructor must not be null"); + .isThrownBy(() -> ResolvableType.forConstructorParameter(null, 0)) + .withMessage("Constructor must not be null"); } @Test @@ -228,8 +228,8 @@ class ResolvableTypeTests { @Test void forMethodParameterByIndexMustNotBeNull() throws Exception { assertThatIllegalArgumentException() - .isThrownBy(() -> ResolvableType.forMethodParameter(null, 0)) - .withMessage("Method must not be null"); + .isThrownBy(() -> ResolvableType.forMethodParameter(null, 0)) + .withMessage("Method must not be null"); } @Test @@ -268,8 +268,8 @@ class ResolvableTypeTests { @Test void forMethodParameterMustNotBeNull() throws Exception { assertThatIllegalArgumentException() - .isThrownBy(() -> ResolvableType.forMethodParameter(null)) - .withMessage("MethodParameter must not be null"); + .isThrownBy(() -> ResolvableType.forMethodParameter(null)) + .withMessage("MethodParameter must not be null"); } @Test // SPR-16210 @@ -295,8 +295,8 @@ class ResolvableTypeTests { @Test void forMethodReturnMustNotBeNull() throws Exception { assertThatIllegalArgumentException() - .isThrownBy(() -> ResolvableType.forMethodReturnType(null)) - .withMessage("Method must not be null"); + .isThrownBy(() -> ResolvableType.forMethodReturnType(null)) + .withMessage("Method must not be null"); } @Test @@ -343,7 +343,7 @@ class ResolvableTypeTests { ResolvableType type = ResolvableType.forField(field); assertThat(type.isArray()).isTrue(); assertThat(type.getComponentType().getType()) - .isEqualTo(((Class) field.getGenericType()).getComponentType()); + .isEqualTo(((Class) field.getGenericType()).getComponentType()); } @Test @@ -686,7 +686,6 @@ class ResolvableTypeTests { assertThat(type.resolve()).isEqualTo(CharSequence.class); } - @Test void resolveBoundedTypeVariableWildcardResult() throws Exception { ResolvableType type = ResolvableType.forMethodReturnType(Methods.class.getMethod("boundedTypeVariableWildcardResult")); @@ -701,30 +700,26 @@ class ResolvableTypeTests { @Test void resolveTypeVariableFromSimpleInterfaceType() { - ResolvableType type = ResolvableType.forClass( - MySimpleInterfaceType.class).as(MyInterfaceType.class); + ResolvableType type = ResolvableType.forClass(MySimpleInterfaceType.class).as(MyInterfaceType.class); assertThat(type.resolveGeneric()).isEqualTo(String.class); } @Test void resolveTypeVariableFromSimpleCollectionInterfaceType() { - ResolvableType type = ResolvableType.forClass( - MyCollectionInterfaceType.class).as(MyInterfaceType.class); + ResolvableType type = ResolvableType.forClass(MyCollectionInterfaceType.class).as(MyInterfaceType.class); assertThat(type.resolveGeneric()).isEqualTo(Collection.class); assertThat(type.resolveGeneric(0, 0)).isEqualTo(String.class); } @Test void resolveTypeVariableFromSimpleSuperclassType() { - ResolvableType type = ResolvableType.forClass( - MySimpleSuperclassType.class).as(MySuperclassType.class); + ResolvableType type = ResolvableType.forClass(MySimpleSuperclassType.class).as(MySuperclassType.class); assertThat(type.resolveGeneric()).isEqualTo(String.class); } @Test void resolveTypeVariableFromSimpleCollectionSuperclassType() { - ResolvableType type = ResolvableType.forClass( - MyCollectionSuperclassType.class).as(MySuperclassType.class); + ResolvableType type = ResolvableType.forClass(MyCollectionSuperclassType.class).as(MySuperclassType.class); assertThat(type.resolveGeneric()).isEqualTo(Collection.class); assertThat(type.resolveGeneric(0, 0)).isEqualTo(String.class); } @@ -751,8 +746,7 @@ class ResolvableTypeTests { void resolveTypeVariableFromSuperType() throws Exception { ResolvableType type = ResolvableType.forClass(ExtendsList.class); assertThat(type.resolve()).isEqualTo(ExtendsList.class); - assertThat(type.asCollection().resolveGeneric()) - .isEqualTo(CharSequence.class); + assertThat(type.asCollection().resolveGeneric()).isEqualTo(CharSequence.class); } @Test @@ -964,8 +958,8 @@ class ResolvableTypeTests { @Test void isAssignableFromMustNotBeNull() throws Exception { assertThatIllegalArgumentException() - .isThrownBy(() -> ResolvableType.forClass(Object.class).isAssignableFrom((ResolvableType) null)) - .withMessage("ResolvableType must not be null"); + .isThrownBy(() -> ResolvableType.forClass(Object.class).isAssignableFrom((ResolvableType) null)) + .withMessage("ResolvableType must not be null"); } @Test @@ -1004,6 +998,7 @@ class ResolvableTypeTests { void isAssignableFromCannotBeResolved() throws Exception { ResolvableType objectType = ResolvableType.forClass(Object.class); ResolvableType unresolvableVariable = ResolvableType.forField(AssignmentBase.class.getField("o")); + assertThat(unresolvableVariable.resolve()).isNull(); assertThatResolvableType(objectType).isAssignableFrom(unresolvableVariable); assertThatResolvableType(unresolvableVariable).isAssignableFrom(objectType); @@ -1220,9 +1215,9 @@ class ResolvableTypeTests { @Test void forClassWithMismatchedGenerics() throws Exception { assertThatIllegalArgumentException() - .isThrownBy(() -> ResolvableType.forClassWithGenerics(Map.class, Integer.class)) - .withMessageContaining("Mismatched number of generics specified for") - .withMessageContaining("java.util.Map"); + .isThrownBy(() -> ResolvableType.forClassWithGenerics(Map.class, Integer.class)) + .withMessageContaining("Mismatched number of generics specified for") + .withMessageContaining("java.util.Map"); } @Test @@ -1277,7 +1272,7 @@ class ResolvableTypeTests { } @Test - void hasUnresolvableGenericsWhenImplementesRawInterface() throws Exception { + void hasUnresolvableGenericsWhenImplementingRawInterface() throws Exception { ResolvableType type = ResolvableType.forClass(MySimpleInterfaceTypeWithImplementsRaw.class); for (ResolvableType generic : type.getGenerics()) { assertThat(generic.resolve()).isNotNull();