Skip to content

Commit 2ec41af

Browse files
committed
ReflectivePropertyAccessor should not consider bridge methods
Issue: SPR-9995 Backport-Issue: SPR-9994
1 parent eb24365 commit 2ec41af

File tree

1 file changed

+99
-91
lines changed

1 file changed

+99
-91
lines changed

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

Lines changed: 99 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,10 @@
3636
import org.springframework.util.StringUtils;
3737

3838
/**
39-
* Simple PropertyAccessor that uses reflection to access properties for reading and writing. A property can be accessed
40-
* if it is accessible as a field on the object or through a getter (if being read) or a setter (if being written).
41-
*
39+
* Simple PropertyAccessor that uses reflection to access properties for reading and writing.
40+
* A property can be accessed if it is accessible as a field on the object or through a
41+
* getter (if being read) or a setter (if being written).
42+
*
4243
* @author Andy Clement
4344
* @author Juergen Hoeller
4445
* @since 3.0
@@ -48,7 +49,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
4849
protected final Map<CacheKey, InvokerPair> readerCache = new ConcurrentHashMap<CacheKey, InvokerPair>();
4950

5051
protected final Map<CacheKey, Member> writerCache = new ConcurrentHashMap<CacheKey, Member>();
51-
52+
5253
protected final Map<CacheKey, TypeDescriptor> typeDescriptorCache = new ConcurrentHashMap<CacheKey, TypeDescriptor>();
5354

5455

@@ -252,7 +253,7 @@ public void write(EvaluationContext context, Object target, String name, Object
252253

253254
throw new AccessException("Neither setter nor field found for property '" + name + "'");
254255
}
255-
256+
256257
private TypeDescriptor getTypeDescriptor(EvaluationContext context, Object target, String name) {
257258
if (target == null) {
258259
return null;
@@ -306,23 +307,24 @@ private Field findField(String name, Class<?> clazz, Object target) {
306307
}
307308

308309
/**
309-
* Find a getter method for the specified property. A getter is defined as a method whose name start with the prefix
310-
* 'get' and the rest of the name is the same as the property name (with the first character uppercased).
310+
* Find a getter method for the specified property.
311+
* <p>A getter is defined as a method whose name start with the prefix 'get' and the
312+
* rest of the name is the same as the property name (with the first character uppercased).
311313
*/
312314
protected Method findGetterForProperty(String propertyName, Class<?> clazz, boolean mustBeStatic) {
313315
Method[] ms = clazz.getMethods();
314316
// Try "get*" method...
315317
String getterName = "get" + StringUtils.capitalize(propertyName);
316318
for (Method method : ms) {
317-
if (method.getName().equals(getterName) && method.getParameterTypes().length == 0 &&
319+
if (!method.isBridge() && method.getName().equals(getterName) && method.getParameterTypes().length == 0 &&
318320
(!mustBeStatic || Modifier.isStatic(method.getModifiers()))) {
319321
return method;
320322
}
321323
}
322324
// Try "is*" method...
323325
getterName = "is" + StringUtils.capitalize(propertyName);
324326
for (Method method : ms) {
325-
if (method.getName().equals(getterName) && method.getParameterTypes().length == 0 &&
327+
if (!method.isBridge() && method.getName().equals(getterName) && method.getParameterTypes().length == 0 &&
326328
boolean.class.equals(method.getReturnType()) &&
327329
(!mustBeStatic || Modifier.isStatic(method.getModifiers()))) {
328330
return method;
@@ -338,7 +340,7 @@ protected Method findSetterForProperty(String propertyName, Class<?> clazz, bool
338340
Method[] methods = clazz.getMethods();
339341
String setterName = "set" + StringUtils.capitalize(propertyName);
340342
for (Method method : methods) {
341-
if (method.getName().equals(setterName) && method.getParameterTypes().length == 1 &&
343+
if (!method.isBridge() && method.getName().equals(setterName) && method.getParameterTypes().length == 1 &&
342344
(!mustBeStatic || Modifier.isStatic(method.getModifiers()))) {
343345
return method;
344346
}
@@ -358,56 +360,10 @@ protected Field findField(String name, Class<?> clazz, boolean mustBeStatic) {
358360
}
359361
return null;
360362
}
361-
362-
/**
363-
* Captures the member (method/field) to call reflectively to access a property value and the type descriptor for the
364-
* value returned by the reflective call.
365-
*/
366-
private static class InvokerPair {
367-
368-
final Member member;
369-
370-
final TypeDescriptor typeDescriptor;
371-
372-
public InvokerPair(Member member, TypeDescriptor typeDescriptor) {
373-
this.member = member;
374-
this.typeDescriptor = typeDescriptor;
375-
}
376-
377-
}
378363

379-
private static class CacheKey {
380-
381-
private final Class clazz;
382-
383-
private final String name;
384-
385-
public CacheKey(Class clazz, String name) {
386-
this.clazz = clazz;
387-
this.name = name;
388-
}
389-
390-
@Override
391-
public boolean equals(Object other) {
392-
if (this == other) {
393-
return true;
394-
}
395-
if (!(other instanceof CacheKey)) {
396-
return false;
397-
}
398-
CacheKey otherKey = (CacheKey) other;
399-
return (this.clazz.equals(otherKey.clazz) && this.name.equals(otherKey.name));
400-
}
401-
402-
@Override
403-
public int hashCode() {
404-
return this.clazz.hashCode() * 29 + this.name.hashCode();
405-
}
406-
}
407-
408-
/**
364+
/**
409365
* Attempt to create an optimized property accessor tailored for a property of a particular name on
410-
* a particular class. The general ReflectivePropertyAccessor will always work but is not optimal
366+
* a particular class. The general ReflectivePropertyAccessor will always work but is not optimal
411367
* due to the need to lookup which reflective member (method/field) to use each time read() is called.
412368
* This method will just return the ReflectivePropertyAccessor instance if it is unable to build
413369
* something more optimal.
@@ -456,37 +412,90 @@ public PropertyAccessor createOptimalAccessor(EvaluationContext eContext, Object
456412
}
457413
return this;
458414
}
459-
415+
416+
417+
/**
418+
* Captures the member (method/field) to call reflectively to access a property value
419+
* and the type descriptor for the value returned by the reflective call.
420+
*/
421+
private static class InvokerPair {
422+
423+
final Member member;
424+
425+
final TypeDescriptor typeDescriptor;
426+
427+
public InvokerPair(Member member, TypeDescriptor typeDescriptor) {
428+
this.member = member;
429+
this.typeDescriptor = typeDescriptor;
430+
}
431+
}
432+
433+
434+
private static class CacheKey {
435+
436+
private final Class clazz;
437+
438+
private final String name;
439+
440+
public CacheKey(Class clazz, String name) {
441+
this.clazz = clazz;
442+
this.name = name;
443+
}
444+
445+
@Override
446+
public boolean equals(Object other) {
447+
if (this == other) {
448+
return true;
449+
}
450+
if (!(other instanceof CacheKey)) {
451+
return false;
452+
}
453+
CacheKey otherKey = (CacheKey) other;
454+
return (this.clazz.equals(otherKey.clazz) && this.name.equals(otherKey.name));
455+
}
456+
457+
@Override
458+
public int hashCode() {
459+
return this.clazz.hashCode() * 29 + this.name.hashCode();
460+
}
461+
}
462+
463+
460464
/**
461-
* An optimized form of a PropertyAccessor that will use reflection but only knows how to access a particular property
462-
* on a particular class. This is unlike the general ReflectivePropertyResolver which manages a cache of methods/fields that
463-
* may be invoked to access different properties on different classes. This optimal accessor exists because looking up
464-
* the appropriate reflective object by class/name on each read is not cheap.
465+
* An optimized form of a PropertyAccessor that will use reflection but only knows
466+
* how to access a particular property on a particular class. This is unlike the
467+
* general ReflectivePropertyResolver which manages a cache of methods/fields that
468+
* may be invoked to access different properties on different classes. This optimal
469+
* accessor exists because looking up the appropriate reflective object by class/name
470+
* on each read is not cheap.
465471
*/
466-
static class OptimalPropertyAccessor implements PropertyAccessor {
472+
private static class OptimalPropertyAccessor implements PropertyAccessor {
473+
467474
private final Member member;
475+
468476
private final TypeDescriptor typeDescriptor;
477+
469478
private final boolean needsToBeMadeAccessible;
470-
479+
471480
OptimalPropertyAccessor(InvokerPair target) {
472-
this.member = target.member;
481+
this.member = target.member;
473482
this.typeDescriptor = target.typeDescriptor;
474483
if (this.member instanceof Field) {
475-
Field field = (Field)member;
476-
needsToBeMadeAccessible = (!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()))
477-
&& !field.isAccessible();
484+
Field field = (Field) this.member;
485+
this.needsToBeMadeAccessible = (!Modifier.isPublic(field.getModifiers()) ||
486+
!Modifier.isPublic(field.getDeclaringClass().getModifiers())) && !field.isAccessible();
478487
}
479488
else {
480-
Method method = (Method)member;
481-
needsToBeMadeAccessible = ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers()))
482-
&& !method.isAccessible());
489+
Method method = (Method) this.member;
490+
this.needsToBeMadeAccessible = ((!Modifier.isPublic(method.getModifiers()) ||
491+
!Modifier.isPublic(method.getDeclaringClass().getModifiers())) && !method.isAccessible());
483492
}
484493
}
485494

486495
public Class[] getSpecificTargetClasses() {
487496
throw new UnsupportedOperationException("Should not be called on an OptimalPropertyAccessor");
488497
}
489-
498+
490499
public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException {
491500
if (target == null) {
492501
return false;
@@ -495,8 +504,8 @@ public boolean canRead(EvaluationContext context, Object target, String name) th
495504
if (type.isArray()) {
496505
return false;
497506
}
498-
if (member instanceof Method) {
499-
Method method = (Method)member;
507+
if (this.member instanceof Method) {
508+
Method method = (Method) this.member;
500509
String getterName = "get" + StringUtils.capitalize(name);
501510
if (getterName.equals(method.getName())) {
502511
return true;
@@ -505,31 +514,31 @@ public boolean canRead(EvaluationContext context, Object target, String name) th
505514
return getterName.equals(method.getName());
506515
}
507516
else {
508-
Field field = (Field)member;
517+
Field field = (Field) this.member;
509518
return field.getName().equals(name);
510519
}
511520
}
512521

513522
public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
514-
if (member instanceof Method) {
523+
if (this.member instanceof Method) {
515524
try {
516-
if (needsToBeMadeAccessible) {
517-
ReflectionUtils.makeAccessible((Method) member);
525+
if (this.needsToBeMadeAccessible) {
526+
ReflectionUtils.makeAccessible((Method) this.member);
518527
}
519-
Object value = ((Method) member).invoke(target);
520-
return new TypedValue(value, typeDescriptor.narrow(value));
528+
Object value = ((Method) this.member).invoke(target);
529+
return new TypedValue(value, this.typeDescriptor.narrow(value));
521530
}
522531
catch (Exception ex) {
523532
throw new AccessException("Unable to access property '" + name + "' through getter", ex);
524533
}
525-
}
526-
if (member instanceof Field) {
534+
}
535+
if (this.member instanceof Field) {
527536
try {
528-
if (needsToBeMadeAccessible) {
529-
ReflectionUtils.makeAccessible((Field)member);
537+
if (this.needsToBeMadeAccessible) {
538+
ReflectionUtils.makeAccessible((Field) this.member);
530539
}
531-
Object value = ((Field)member).get(target);
532-
return new TypedValue(value, typeDescriptor.narrow(value));
540+
Object value = ((Field) this.member).get(target);
541+
return new TypedValue(value, this.typeDescriptor.narrow(value));
533542
}
534543
catch (Exception ex) {
535544
throw new AccessException("Unable to access field: " + name, ex);
@@ -538,12 +547,11 @@ public TypedValue read(EvaluationContext context, Object target, String name) th
538547
throw new AccessException("Neither getter nor field found for property '" + name + "'");
539548
}
540549

541-
public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException {
550+
public boolean canWrite(EvaluationContext context, Object target, String name) {
542551
throw new UnsupportedOperationException("Should not be called on an OptimalPropertyAccessor");
543552
}
544-
545-
public void write(EvaluationContext context, Object target, String name, Object newValue)
546-
throws AccessException {
553+
554+
public void write(EvaluationContext context, Object target, String name, Object newValue) {
547555
throw new UnsupportedOperationException("Should not be called on an OptimalPropertyAccessor");
548556
}
549557
}

0 commit comments

Comments
 (0)