Merge branch '6.0.x'

# Conflicts:
#	spring-beans/src/main/java/org/springframework/beans/BeanUtils.java
#	spring-core/src/main/java/org/springframework/core/ResolvableType.java
#	spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java
This commit is contained in:
Juergen Hoeller 2023-10-24 22:58:31 +02:00
commit 93b0b66735
7 changed files with 53 additions and 35 deletions

View File

@ -1013,18 +1013,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;
}

View File

@ -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.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
@ -613,8 +614,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();
@ -781,38 +782,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<String> 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) {
@ -826,6 +817,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.
@ -899,7 +908,6 @@ public abstract class BeanUtils {
}
return kotlinConstructor.callBy(argParameters);
}
}
}

View File

@ -144,7 +144,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);

View File

@ -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 <T> GenericTestEvent<T> createGenericTestEvent(T payload) {
return new GenericTestEvent<>(this, payload);
}
protected static class GenericTestEvent<T> extends ApplicationEvent {
@ -51,6 +55,7 @@ public abstract class AbstractApplicationEventListenerTests {
}
}
protected static class SmartGenericTestEvent<T> extends GenericTestEvent<T> implements ResolvableTypeProvider {
private final ResolvableType resolvableType;
@ -67,6 +72,7 @@ public abstract class AbstractApplicationEventListenerTests {
}
}
protected static class StringEvent extends GenericTestEvent<String> {
public StringEvent(Object source, String payload) {
@ -74,6 +80,7 @@ public abstract class AbstractApplicationEventListenerTests {
}
}
protected static class LongEvent extends GenericTestEvent<Long> {
public LongEvent(Object source, Long payload) {
@ -81,31 +88,31 @@ public abstract class AbstractApplicationEventListenerTests {
}
}
protected <T> GenericTestEvent<T> createGenericTestEvent(T payload) {
return new GenericTestEvent<>(this, payload);
}
static class GenericEventListener implements ApplicationListener<GenericTestEvent<?>> {
@Override
public void onApplicationEvent(GenericTestEvent<?> event) {
}
}
static class ObjectEventListener implements ApplicationListener<GenericTestEvent<Object>> {
@Override
public void onApplicationEvent(GenericTestEvent<Object> event) {
}
}
static class UpperBoundEventListener
implements ApplicationListener<GenericTestEvent<? extends RuntimeException>> {
static class UpperBoundEventListener implements ApplicationListener<GenericTestEvent<? extends RuntimeException>> {
@Override
public void onApplicationEvent(GenericTestEvent<? extends RuntimeException> event) {
}
}
static class StringEventListener implements ApplicationListener<GenericTestEvent<String>> {
@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;
}
}

View File

@ -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

View File

@ -714,12 +714,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...)

View File

@ -360,7 +360,7 @@ class ResolvableTypeTests {
ResolvableType type = ResolvableType.forField(field);
assertThat(type.isArray()).isTrue();
assertThat(type.getComponentType().getType())
.isEqualTo(((Class) field.getGenericType()).componentType());
.isEqualTo(((Class) field.getGenericType()).componentType());
}
@Test