Skip to content

Commit a1eae42

Browse files
committed
Expose implementation method for annotation introspection purposes
Closes gh-23210
1 parent 56cc0d0 commit a1eae42

File tree

2 files changed

+51
-22
lines changed

2 files changed

+51
-22
lines changed

spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ public boolean canRead(EvaluationContext context, @Nullable Object target, Strin
138138
// The readerCache will only contain gettable properties (let's not worry about setters for now).
139139
Property property = new Property(type, method, null);
140140
TypeDescriptor typeDescriptor = new TypeDescriptor(property);
141+
method = ClassUtils.getInterfaceMethodIfPossible(method);
141142
this.readerCache.put(cacheKey, new InvokerPair(method, typeDescriptor));
142143
this.typeDescriptorCache.put(cacheKey, typeDescriptor);
143144
return true;
@@ -180,6 +181,7 @@ public TypedValue read(EvaluationContext context, @Nullable Object target, Strin
180181
// The readerCache will only contain gettable properties (let's not worry about setters for now).
181182
Property property = new Property(type, method, null);
182183
TypeDescriptor typeDescriptor = new TypeDescriptor(property);
184+
method = ClassUtils.getInterfaceMethodIfPossible(method);
183185
invoker = new InvokerPair(method, typeDescriptor);
184186
this.lastReadInvokerPair = invoker;
185187
this.readerCache.put(cacheKey, invoker);
@@ -239,6 +241,7 @@ public boolean canWrite(EvaluationContext context, @Nullable Object target, Stri
239241
// Treat it like a property
240242
Property property = new Property(type, null, method);
241243
TypeDescriptor typeDescriptor = new TypeDescriptor(property);
244+
method = ClassUtils.getInterfaceMethodIfPossible(method);
242245
this.writerCache.put(cacheKey, method);
243246
this.typeDescriptorCache.put(cacheKey, typeDescriptor);
244247
return true;
@@ -287,6 +290,7 @@ public void write(EvaluationContext context, @Nullable Object target, String nam
287290
if (method == null) {
288291
method = findSetterForProperty(name, type, target);
289292
if (method != null) {
293+
method = ClassUtils.getInterfaceMethodIfPossible(method);
290294
cachedMember = method;
291295
this.writerCache.put(cacheKey, cachedMember);
292296
}
@@ -414,13 +418,24 @@ private Method findMethodForProperty(String[] methodSuffixes, String prefix, Cla
414418
method.getParameterCount() == numberOfParams &&
415419
(!mustBeStatic || Modifier.isStatic(method.getModifiers())) &&
416420
(requiredReturnTypes.isEmpty() || requiredReturnTypes.contains(method.getReturnType()))) {
417-
return ClassUtils.getInterfaceMethodIfPossible(method);
421+
return method;
418422
}
419423
}
420424
}
421425
return null;
422426
}
423427

428+
/**
429+
* Return class methods ordered with non-bridge methods appearing higher.
430+
*/
431+
private Method[] getSortedMethods(Class<?> clazz) {
432+
return this.sortedMethodsCache.computeIfAbsent(clazz, key -> {
433+
Method[] methods = key.getMethods();
434+
Arrays.sort(methods, (o1, o2) -> (o1.isBridge() == o2.isBridge() ? 0 : (o1.isBridge() ? 1 : -1)));
435+
return methods;
436+
});
437+
}
438+
424439
/**
425440
* Determine whether the given {@code Method} is a candidate for property access
426441
* on an instance of the given target class.
@@ -434,17 +449,6 @@ protected boolean isCandidateForProperty(Method method, Class<?> targetClass) {
434449
return true;
435450
}
436451

437-
/**
438-
* Return class methods ordered with non-bridge methods appearing higher.
439-
*/
440-
private Method[] getSortedMethods(Class<?> clazz) {
441-
return this.sortedMethodsCache.computeIfAbsent(clazz, key -> {
442-
Method[] methods = key.getMethods();
443-
Arrays.sort(methods, (o1, o2) -> (o1.isBridge() == o2.isBridge() ? 0 : (o1.isBridge() ? 1 : -1)));
444-
return methods;
445-
});
446-
}
447-
448452
/**
449453
* Return the method suffixes for a given property name. The default implementation
450454
* uses JavaBean conventions with additional support for properties of the form 'xY'
@@ -536,7 +540,9 @@ public PropertyAccessor createOptimalAccessor(EvaluationContext context, @Nullab
536540
if (method == null) {
537541
method = findGetterForProperty(name, clazz, target);
538542
if (method != null) {
539-
invocationTarget = new InvokerPair(method, new TypeDescriptor(new MethodParameter(method, -1)));
543+
TypeDescriptor typeDescriptor = new TypeDescriptor(new MethodParameter(method, -1));
544+
method = ClassUtils.getInterfaceMethodIfPossible(method);
545+
invocationTarget = new InvokerPair(method, typeDescriptor);
540546
ReflectionUtils.makeAccessible(method);
541547
this.readerCache.put(cacheKey, invocationTarget);
542548
}

spring-expression/src/test/java/org/springframework/expression/spel/SpelReproTests.java

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -60,10 +60,18 @@
6060
import org.springframework.expression.spel.support.StandardEvaluationContext;
6161
import org.springframework.expression.spel.support.StandardTypeLocator;
6262
import org.springframework.expression.spel.testresources.le.div.mod.reserved.Reserver;
63+
import org.springframework.lang.Nullable;
6364
import org.springframework.util.ObjectUtils;
6465

65-
import static org.hamcrest.Matchers.*;
66-
import static org.junit.Assert.*;
66+
import static org.hamcrest.Matchers.equalTo;
67+
import static org.hamcrest.Matchers.is;
68+
import static org.junit.Assert.assertEquals;
69+
import static org.junit.Assert.assertFalse;
70+
import static org.junit.Assert.assertNotNull;
71+
import static org.junit.Assert.assertNull;
72+
import static org.junit.Assert.assertThat;
73+
import static org.junit.Assert.assertTrue;
74+
import static org.junit.Assert.fail;
6775

6876
/**
6977
* Reproduction tests cornering various reported SpEL issues.
@@ -1213,9 +1221,13 @@ public void SPR9486_floatPowerDouble() {
12131221
public void SPR9994_bridgeMethods() throws Exception {
12141222
ReflectivePropertyAccessor accessor = new ReflectivePropertyAccessor();
12151223
StandardEvaluationContext context = new StandardEvaluationContext();
1216-
Object target = new GenericImplementation();
1224+
GenericImplementation target = new GenericImplementation();
1225+
accessor.write(context, target, "property", "1");
1226+
assertEquals(1, target.value);
12171227
TypedValue value = accessor.read(context, target, "property");
1228+
assertEquals(1, value.getValue());
12181229
assertEquals(Integer.class, value.getTypeDescriptor().getType());
1230+
assertTrue(value.getTypeDescriptor().getAnnotations().length > 0);
12191231
}
12201232

12211233
@Test
@@ -1224,6 +1236,7 @@ public void SPR10162_onlyBridgeMethod() throws Exception {
12241236
StandardEvaluationContext context = new StandardEvaluationContext();
12251237
Object target = new OnlyBridgeMethod();
12261238
TypedValue value = accessor.read(context, target, "property");
1239+
assertNull(value.getValue());
12271240
assertEquals(Integer.class, value.getTypeDescriptor().getType());
12281241
}
12291242

@@ -1232,31 +1245,31 @@ public void SPR10091_simpleTestValueType() {
12321245
ExpressionParser parser = new SpelExpressionParser();
12331246
StandardEvaluationContext evaluationContext = new StandardEvaluationContext(new BooleanHolder());
12341247
Class<?> valueType = parser.parseExpression("simpleProperty").getValueType(evaluationContext);
1235-
assertNotNull(valueType);
1248+
assertEquals(Boolean.class, valueType);
12361249
}
12371250

12381251
@Test
12391252
public void SPR10091_simpleTestValue() {
12401253
ExpressionParser parser = new SpelExpressionParser();
12411254
StandardEvaluationContext evaluationContext = new StandardEvaluationContext(new BooleanHolder());
12421255
Object value = parser.parseExpression("simpleProperty").getValue(evaluationContext);
1243-
assertNotNull(value);
1256+
assertEquals(Boolean.class, value.getClass());
12441257
}
12451258

12461259
@Test
12471260
public void SPR10091_primitiveTestValueType() {
12481261
ExpressionParser parser = new SpelExpressionParser();
12491262
StandardEvaluationContext evaluationContext = new StandardEvaluationContext(new BooleanHolder());
12501263
Class<?> valueType = parser.parseExpression("primitiveProperty").getValueType(evaluationContext);
1251-
assertNotNull(valueType);
1264+
assertEquals(Boolean.class, valueType);
12521265
}
12531266

12541267
@Test
12551268
public void SPR10091_primitiveTestValue() {
12561269
ExpressionParser parser = new SpelExpressionParser();
12571270
StandardEvaluationContext evaluationContext = new StandardEvaluationContext(new BooleanHolder());
12581271
Object value = parser.parseExpression("primitiveProperty").getValue(evaluationContext);
1259-
assertNotNull(value);
1272+
assertEquals(Boolean.class, value.getClass());
12601273
}
12611274

12621275
@Test
@@ -2220,15 +2233,25 @@ public boolean isPrimitiveProperty() {
22202233

22212234
private interface GenericInterface<T extends Number> {
22222235

2236+
void setProperty(T value);
2237+
22232238
T getProperty();
22242239
}
22252240

22262241

22272242
private static class GenericImplementation implements GenericInterface<Integer> {
22282243

2244+
int value;
2245+
2246+
@Override
2247+
public void setProperty(Integer value) {
2248+
this.value = value;
2249+
}
2250+
22292251
@Override
2252+
@Nullable
22302253
public Integer getProperty() {
2231-
return null;
2254+
return this.value;
22322255
}
22332256
}
22342257

0 commit comments

Comments
 (0)