Skip to content

Commit

Permalink
Expose implementation method for annotation introspection purposes
Browse files Browse the repository at this point in the history
Closes gh-23210
  • Loading branch information
jhoeller committed Jul 5, 2019
1 parent 56cc0d0 commit a1eae42
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ public boolean canRead(EvaluationContext context, @Nullable Object target, Strin
// 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;
Expand Down Expand Up @@ -180,6 +181,7 @@ public TypedValue read(EvaluationContext context, @Nullable Object target, Strin
// 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);
Expand Down Expand Up @@ -239,6 +241,7 @@ public boolean canWrite(EvaluationContext context, @Nullable Object target, Stri
// 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;
Expand Down Expand Up @@ -287,6 +290,7 @@ public void write(EvaluationContext context, @Nullable Object target, String nam
if (method == null) {
method = findSetterForProperty(name, type, target);
if (method != null) {
method = ClassUtils.getInterfaceMethodIfPossible(method);
cachedMember = method;
this.writerCache.put(cacheKey, cachedMember);
}
Expand Down Expand Up @@ -414,13 +418,24 @@ private Method findMethodForProperty(String[] methodSuffixes, String prefix, Cla
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.
Expand All @@ -434,17 +449,6 @@ protected boolean isCandidateForProperty(Method method, Class<?> targetClass) {
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'
Expand Down Expand Up @@ -536,7 +540,9 @@ public PropertyAccessor createOptimalAccessor(EvaluationContext context, @Nullab
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);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -60,10 +60,18 @@
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.
Expand Down Expand Up @@ -1213,9 +1221,13 @@ public void SPR9486_floatPowerDouble() {
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
Expand All @@ -1224,6 +1236,7 @@ public void SPR10162_onlyBridgeMethod() throws Exception {
StandardEvaluationContext context = new StandardEvaluationContext();
Object target = new OnlyBridgeMethod();
TypedValue value = accessor.read(context, target, "property");
assertNull(value.getValue());
assertEquals(Integer.class, value.getTypeDescriptor().getType());
}

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

@Test
public void SPR10091_simpleTestValue() {
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
public void SPR10091_primitiveTestValueType() {
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext evaluationContext = new StandardEvaluationContext(new BooleanHolder());
Class<?> valueType = parser.parseExpression("primitiveProperty").getValueType(evaluationContext);
assertNotNull(valueType);
assertEquals(Boolean.class, valueType);
}

@Test
public void SPR10091_primitiveTestValue() {
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
Expand Down Expand Up @@ -2220,15 +2233,25 @@ public boolean isPrimitiveProperty() {

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;
}
}

Expand Down

0 comments on commit a1eae42

Please sign in to comment.