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:
@@ -1013,18 +1013,20 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA
|
|||||||
*/
|
*/
|
||||||
protected abstract static class PropertyHandler {
|
protected abstract static class PropertyHandler {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
private final Class<?> propertyType;
|
private final Class<?> propertyType;
|
||||||
|
|
||||||
private final boolean readable;
|
private final boolean readable;
|
||||||
|
|
||||||
private final boolean writable;
|
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.propertyType = propertyType;
|
||||||
this.readable = readable;
|
this.readable = readable;
|
||||||
this.writable = writable;
|
this.writable = writable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
public Class<?> getPropertyType() {
|
public Class<?> getPropertyType() {
|
||||||
return this.propertyType;
|
return this.propertyType;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import java.lang.reflect.Constructor;
|
|||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@@ -613,8 +614,8 @@ public abstract class BeanUtils {
|
|||||||
* @return a corresponding MethodParameter object
|
* @return a corresponding MethodParameter object
|
||||||
*/
|
*/
|
||||||
public static MethodParameter getWriteMethodParameter(PropertyDescriptor pd) {
|
public static MethodParameter getWriteMethodParameter(PropertyDescriptor pd) {
|
||||||
if (pd instanceof GenericTypeAwarePropertyDescriptor typeAwarePd) {
|
if (pd instanceof GenericTypeAwarePropertyDescriptor gpd) {
|
||||||
return new MethodParameter(typeAwarePd.getWriteMethodParameter());
|
return new MethodParameter(gpd.getWriteMethodParameter());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Method writeMethod = pd.getWriteMethod();
|
Method writeMethod = pd.getWriteMethod();
|
||||||
@@ -781,38 +782,28 @@ public abstract class BeanUtils {
|
|||||||
if (editable != null) {
|
if (editable != null) {
|
||||||
if (!editable.isInstance(target)) {
|
if (!editable.isInstance(target)) {
|
||||||
throw new IllegalArgumentException("Target class [" + target.getClass().getName() +
|
throw new IllegalArgumentException("Target class [" + target.getClass().getName() +
|
||||||
"] not assignable to Editable class [" + editable.getName() + "]");
|
"] not assignable to editable class [" + editable.getName() + "]");
|
||||||
}
|
}
|
||||||
actualEditable = editable;
|
actualEditable = editable;
|
||||||
}
|
}
|
||||||
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
|
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
|
||||||
Set<String> ignoredProps = (ignoreProperties != null ? new HashSet<>(Arrays.asList(ignoreProperties)) : null);
|
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) {
|
for (PropertyDescriptor targetPd : targetPds) {
|
||||||
Method writeMethod = targetPd.getWriteMethod();
|
Method writeMethod = targetPd.getWriteMethod();
|
||||||
if (writeMethod != null && (ignoredProps == null || !ignoredProps.contains(targetPd.getName()))) {
|
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) {
|
if (sourcePd != null) {
|
||||||
Method readMethod = sourcePd.getReadMethod();
|
Method readMethod = sourcePd.getReadMethod();
|
||||||
if (readMethod != null) {
|
if (readMethod != null) {
|
||||||
ResolvableType sourceResolvableType = ResolvableType.forMethodReturnType(readMethod);
|
if (isAssignable(writeMethod, 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) {
|
|
||||||
try {
|
try {
|
||||||
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
|
ReflectionUtils.makeAccessible(readMethod);
|
||||||
readMethod.setAccessible(true);
|
|
||||||
}
|
|
||||||
Object value = readMethod.invoke(source);
|
Object value = readMethod.invoke(source);
|
||||||
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
|
ReflectionUtils.makeAccessible(writeMethod);
|
||||||
writeMethod.setAccessible(true);
|
|
||||||
}
|
|
||||||
writeMethod.invoke(target, value);
|
writeMethod.invoke(target, value);
|
||||||
}
|
}
|
||||||
catch (Throwable ex) {
|
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.
|
* Inner class to avoid a hard dependency on Kotlin at runtime.
|
||||||
@@ -899,7 +908,6 @@ public abstract class BeanUtils {
|
|||||||
}
|
}
|
||||||
return kotlinConstructor.callBy(argParameters);
|
return kotlinConstructor.callBy(argParameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -144,7 +144,6 @@ public class DirectFieldAccessor extends AbstractNestablePropertyAccessor {
|
|||||||
ReflectionUtils.makeAccessible(this.field);
|
ReflectionUtils.makeAccessible(this.field);
|
||||||
return this.field.get(getWrappedInstance());
|
return this.field.get(getWrappedInstance());
|
||||||
}
|
}
|
||||||
|
|
||||||
catch (IllegalAccessException ex) {
|
catch (IllegalAccessException ex) {
|
||||||
throw new InvalidPropertyException(getWrappedClass(),
|
throw new InvalidPropertyException(getWrappedClass(),
|
||||||
this.field.getName(), "Field is not accessible", ex);
|
this.field.getName(), "Field is not accessible", ex);
|
||||||
|
|||||||
@@ -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");
|
* 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.
|
||||||
@@ -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 {
|
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 {
|
protected static class SmartGenericTestEvent<T> extends GenericTestEvent<T> implements ResolvableTypeProvider {
|
||||||
|
|
||||||
private final ResolvableType resolvableType;
|
private final ResolvableType resolvableType;
|
||||||
@@ -67,6 +72,7 @@ public abstract class AbstractApplicationEventListenerTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected static class StringEvent extends GenericTestEvent<String> {
|
protected static class StringEvent extends GenericTestEvent<String> {
|
||||||
|
|
||||||
public StringEvent(Object source, String payload) {
|
public StringEvent(Object source, String payload) {
|
||||||
@@ -74,6 +80,7 @@ public abstract class AbstractApplicationEventListenerTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected static class LongEvent extends GenericTestEvent<Long> {
|
protected static class LongEvent extends GenericTestEvent<Long> {
|
||||||
|
|
||||||
public LongEvent(Object source, Long payload) {
|
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<?>> {
|
static class GenericEventListener implements ApplicationListener<GenericTestEvent<?>> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onApplicationEvent(GenericTestEvent<?> event) {
|
public void onApplicationEvent(GenericTestEvent<?> event) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static class ObjectEventListener implements ApplicationListener<GenericTestEvent<Object>> {
|
static class ObjectEventListener implements ApplicationListener<GenericTestEvent<Object>> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onApplicationEvent(GenericTestEvent<Object> event) {
|
public void onApplicationEvent(GenericTestEvent<Object> event) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static class UpperBoundEventListener
|
|
||||||
implements ApplicationListener<GenericTestEvent<? extends RuntimeException>> {
|
static class UpperBoundEventListener implements ApplicationListener<GenericTestEvent<? extends RuntimeException>> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onApplicationEvent(GenericTestEvent<? extends RuntimeException> event) {
|
public void onApplicationEvent(GenericTestEvent<? extends RuntimeException> event) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static class StringEventListener implements ApplicationListener<GenericTestEvent<String>> {
|
static class StringEventListener implements ApplicationListener<GenericTestEvent<String>> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -113,6 +120,7 @@ public abstract class AbstractApplicationEventListenerTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
static class RawApplicationListener implements ApplicationListener {
|
static class RawApplicationListener implements ApplicationListener {
|
||||||
|
|
||||||
@@ -121,10 +129,10 @@ public abstract class AbstractApplicationEventListenerTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static class TestEvents {
|
static class TestEvents {
|
||||||
|
|
||||||
public GenericTestEvent<?> wildcardEvent;
|
public GenericTestEvent<?> wildcardEvent;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,7 +104,8 @@ public class GenericApplicationListenerAdapterTests extends AbstractApplicationE
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void genericListenerStrictTypeSubClass() {
|
public void genericListenerStrictTypeSubClass() {
|
||||||
supportsEventType(false, ObjectEventListener.class, ResolvableType.forClassWithGenerics(GenericTestEvent.class, Long.class));
|
supportsEventType(false, ObjectEventListener.class,
|
||||||
|
ResolvableType.forClassWithGenerics(GenericTestEvent.class, Long.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -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
|
* 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
|
* access a specific generic consider using the {@link #getGeneric(int...)} method as
|
||||||
* it allows access to nested generics and protects against
|
* it allows access to nested generics and protects against
|
||||||
* {@code IndexOutOfBoundsExceptions}.
|
* {@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})
|
* (never {@code null})
|
||||||
* @see #hasGenerics()
|
* @see #hasGenerics()
|
||||||
* @see #getGeneric(int...)
|
* @see #getGeneric(int...)
|
||||||
|
|||||||
@@ -360,7 +360,7 @@ class ResolvableTypeTests {
|
|||||||
ResolvableType type = ResolvableType.forField(field);
|
ResolvableType type = ResolvableType.forField(field);
|
||||||
assertThat(type.isArray()).isTrue();
|
assertThat(type.isArray()).isTrue();
|
||||||
assertThat(type.getComponentType().getType())
|
assertThat(type.getComponentType().getType())
|
||||||
.isEqualTo(((Class) field.getGenericType()).componentType());
|
.isEqualTo(((Class) field.getGenericType()).componentType());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user