Expose implementation method for annotation introspection purposes
Closes gh-23210
This commit is contained in:
parent
56cc0d02e9
commit
a1eae42fd0
|
|
@ -138,6 +138,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
|||
// The readerCache will only contain gettable properties (let's not worry about setters for now).
|
||||
Property property = new Property(type, method, null);
|
||||
TypeDescriptor typeDescriptor = new TypeDescriptor(property);
|
||||
method = ClassUtils.getInterfaceMethodIfPossible(method);
|
||||
this.readerCache.put(cacheKey, new InvokerPair(method, typeDescriptor));
|
||||
this.typeDescriptorCache.put(cacheKey, typeDescriptor);
|
||||
return true;
|
||||
|
|
@ -180,6 +181,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
|||
// The readerCache will only contain gettable properties (let's not worry about setters for now).
|
||||
Property property = new Property(type, method, null);
|
||||
TypeDescriptor typeDescriptor = new TypeDescriptor(property);
|
||||
method = ClassUtils.getInterfaceMethodIfPossible(method);
|
||||
invoker = new InvokerPair(method, typeDescriptor);
|
||||
this.lastReadInvokerPair = invoker;
|
||||
this.readerCache.put(cacheKey, invoker);
|
||||
|
|
@ -239,6 +241,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
|||
// Treat it like a property
|
||||
Property property = new Property(type, null, method);
|
||||
TypeDescriptor typeDescriptor = new TypeDescriptor(property);
|
||||
method = ClassUtils.getInterfaceMethodIfPossible(method);
|
||||
this.writerCache.put(cacheKey, method);
|
||||
this.typeDescriptorCache.put(cacheKey, typeDescriptor);
|
||||
return true;
|
||||
|
|
@ -287,6 +290,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
|||
if (method == null) {
|
||||
method = findSetterForProperty(name, type, target);
|
||||
if (method != null) {
|
||||
method = ClassUtils.getInterfaceMethodIfPossible(method);
|
||||
cachedMember = method;
|
||||
this.writerCache.put(cacheKey, cachedMember);
|
||||
}
|
||||
|
|
@ -414,13 +418,24 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
|||
method.getParameterCount() == numberOfParams &&
|
||||
(!mustBeStatic || Modifier.isStatic(method.getModifiers())) &&
|
||||
(requiredReturnTypes.isEmpty() || requiredReturnTypes.contains(method.getReturnType()))) {
|
||||
return ClassUtils.getInterfaceMethodIfPossible(method);
|
||||
return method;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return class methods ordered with non-bridge methods appearing higher.
|
||||
*/
|
||||
private Method[] getSortedMethods(Class<?> clazz) {
|
||||
return this.sortedMethodsCache.computeIfAbsent(clazz, key -> {
|
||||
Method[] methods = key.getMethods();
|
||||
Arrays.sort(methods, (o1, o2) -> (o1.isBridge() == o2.isBridge() ? 0 : (o1.isBridge() ? 1 : -1)));
|
||||
return methods;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the given {@code Method} is a candidate for property access
|
||||
* on an instance of the given target class.
|
||||
|
|
@ -434,17 +449,6 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return class methods ordered with non-bridge methods appearing higher.
|
||||
*/
|
||||
private Method[] getSortedMethods(Class<?> clazz) {
|
||||
return this.sortedMethodsCache.computeIfAbsent(clazz, key -> {
|
||||
Method[] methods = key.getMethods();
|
||||
Arrays.sort(methods, (o1, o2) -> (o1.isBridge() == o2.isBridge() ? 0 : (o1.isBridge() ? 1 : -1)));
|
||||
return methods;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the method suffixes for a given property name. The default implementation
|
||||
* uses JavaBean conventions with additional support for properties of the form 'xY'
|
||||
|
|
@ -536,7 +540,9 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
|||
if (method == null) {
|
||||
method = findGetterForProperty(name, clazz, target);
|
||||
if (method != null) {
|
||||
invocationTarget = new InvokerPair(method, new TypeDescriptor(new MethodParameter(method, -1)));
|
||||
TypeDescriptor typeDescriptor = new TypeDescriptor(new MethodParameter(method, -1));
|
||||
method = ClassUtils.getInterfaceMethodIfPossible(method);
|
||||
invocationTarget = new InvokerPair(method, typeDescriptor);
|
||||
ReflectionUtils.makeAccessible(method);
|
||||
this.readerCache.put(cacheKey, invocationTarget);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2019 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.
|
||||
|
|
@ -60,10 +60,18 @@ import org.springframework.expression.spel.support.ReflectivePropertyAccessor;
|
|||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.expression.spel.support.StandardTypeLocator;
|
||||
import org.springframework.expression.spel.testresources.le.div.mod.reserved.Reserver;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
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.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* Reproduction tests cornering various reported SpEL issues.
|
||||
|
|
@ -1213,9 +1221,13 @@ public class SpelReproTests extends AbstractExpressionTests {
|
|||
public void SPR9994_bridgeMethods() throws Exception {
|
||||
ReflectivePropertyAccessor accessor = new ReflectivePropertyAccessor();
|
||||
StandardEvaluationContext context = new StandardEvaluationContext();
|
||||
Object target = new GenericImplementation();
|
||||
GenericImplementation target = new GenericImplementation();
|
||||
accessor.write(context, target, "property", "1");
|
||||
assertEquals(1, target.value);
|
||||
TypedValue value = accessor.read(context, target, "property");
|
||||
assertEquals(1, value.getValue());
|
||||
assertEquals(Integer.class, value.getTypeDescriptor().getType());
|
||||
assertTrue(value.getTypeDescriptor().getAnnotations().length > 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -1224,6 +1236,7 @@ public class SpelReproTests extends AbstractExpressionTests {
|
|||
StandardEvaluationContext context = new StandardEvaluationContext();
|
||||
Object target = new OnlyBridgeMethod();
|
||||
TypedValue value = accessor.read(context, target, "property");
|
||||
assertNull(value.getValue());
|
||||
assertEquals(Integer.class, value.getTypeDescriptor().getType());
|
||||
}
|
||||
|
||||
|
|
@ -1232,7 +1245,7 @@ public class SpelReproTests extends AbstractExpressionTests {
|
|||
ExpressionParser parser = new SpelExpressionParser();
|
||||
StandardEvaluationContext evaluationContext = new StandardEvaluationContext(new BooleanHolder());
|
||||
Class<?> valueType = parser.parseExpression("simpleProperty").getValueType(evaluationContext);
|
||||
assertNotNull(valueType);
|
||||
assertEquals(Boolean.class, valueType);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -1240,7 +1253,7 @@ public class SpelReproTests extends AbstractExpressionTests {
|
|||
ExpressionParser parser = new SpelExpressionParser();
|
||||
StandardEvaluationContext evaluationContext = new StandardEvaluationContext(new BooleanHolder());
|
||||
Object value = parser.parseExpression("simpleProperty").getValue(evaluationContext);
|
||||
assertNotNull(value);
|
||||
assertEquals(Boolean.class, value.getClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -1248,7 +1261,7 @@ public class SpelReproTests extends AbstractExpressionTests {
|
|||
ExpressionParser parser = new SpelExpressionParser();
|
||||
StandardEvaluationContext evaluationContext = new StandardEvaluationContext(new BooleanHolder());
|
||||
Class<?> valueType = parser.parseExpression("primitiveProperty").getValueType(evaluationContext);
|
||||
assertNotNull(valueType);
|
||||
assertEquals(Boolean.class, valueType);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -1256,7 +1269,7 @@ public class SpelReproTests extends AbstractExpressionTests {
|
|||
ExpressionParser parser = new SpelExpressionParser();
|
||||
StandardEvaluationContext evaluationContext = new StandardEvaluationContext(new BooleanHolder());
|
||||
Object value = parser.parseExpression("primitiveProperty").getValue(evaluationContext);
|
||||
assertNotNull(value);
|
||||
assertEquals(Boolean.class, value.getClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -2220,15 +2233,25 @@ public class SpelReproTests extends AbstractExpressionTests {
|
|||
|
||||
private interface GenericInterface<T extends Number> {
|
||||
|
||||
void setProperty(T value);
|
||||
|
||||
T getProperty();
|
||||
}
|
||||
|
||||
|
||||
private static class GenericImplementation implements GenericInterface<Integer> {
|
||||
|
||||
int value;
|
||||
|
||||
@Override
|
||||
public void setProperty(Integer value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Integer getProperty() {
|
||||
return null;
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue