提交 6d882b14 编写于 作者: P Phillip Webb

Add targetIsClass to SpEL property cache key

Update the `CacheKey` class used by `ReflectivePropertyAccessor` to
include if the target object is class. The prevents an incorrect cache
hit from being returned when a property with the same name is read on
both an object and its class. For example:

	#{class.name}
	#{name}

Issue: SPR-10486
上级 7658d856
...@@ -29,6 +29,7 @@ import java.util.concurrent.ConcurrentHashMap; ...@@ -29,6 +29,7 @@ import java.util.concurrent.ConcurrentHashMap;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.core.convert.Property; import org.springframework.core.convert.Property;
import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.style.ToStringCreator;
import org.springframework.expression.AccessException; import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext; import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException; import org.springframework.expression.EvaluationException;
...@@ -73,7 +74,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor { ...@@ -73,7 +74,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
if (type.isArray() && name.equals("length")) { if (type.isArray() && name.equals("length")) {
return true; return true;
} }
CacheKey cacheKey = new CacheKey(type, name); CacheKey cacheKey = new CacheKey(type, name, target instanceof Class);
if (this.readerCache.containsKey(cacheKey)) { if (this.readerCache.containsKey(cacheKey)) {
return true; return true;
} }
...@@ -113,7 +114,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor { ...@@ -113,7 +114,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
return new TypedValue(Array.getLength(target)); return new TypedValue(Array.getLength(target));
} }
CacheKey cacheKey = new CacheKey(type, name); CacheKey cacheKey = new CacheKey(type, name, target instanceof Class);
InvokerPair invoker = this.readerCache.get(cacheKey); InvokerPair invoker = this.readerCache.get(cacheKey);
if (invoker == null || invoker.member instanceof Method) { if (invoker == null || invoker.member instanceof Method) {
...@@ -172,7 +173,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor { ...@@ -172,7 +173,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
return false; return false;
} }
Class<?> type = (target instanceof Class ? (Class<?>) target : target.getClass()); Class<?> type = (target instanceof Class ? (Class<?>) target : target.getClass());
CacheKey cacheKey = new CacheKey(type, name); CacheKey cacheKey = new CacheKey(type, name, target instanceof Class);
if (this.writerCache.containsKey(cacheKey)) { if (this.writerCache.containsKey(cacheKey)) {
return true; return true;
} }
...@@ -214,7 +215,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor { ...@@ -214,7 +215,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
throw new AccessException("Type conversion failure",evaluationException); throw new AccessException("Type conversion failure",evaluationException);
} }
} }
CacheKey cacheKey = new CacheKey(type, name); CacheKey cacheKey = new CacheKey(type, name, target instanceof Class);
Member cachedMember = this.writerCache.get(cacheKey); Member cachedMember = this.writerCache.get(cacheKey);
if (cachedMember == null || cachedMember instanceof Method) { if (cachedMember == null || cachedMember instanceof Method) {
...@@ -271,7 +272,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor { ...@@ -271,7 +272,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
if (type.isArray() && name.equals("length")) { if (type.isArray() && name.equals("length")) {
return TypeDescriptor.valueOf(Integer.TYPE); return TypeDescriptor.valueOf(Integer.TYPE);
} }
CacheKey cacheKey = new CacheKey(type, name); CacheKey cacheKey = new CacheKey(type, name, target instanceof Class);
TypeDescriptor typeDescriptor = this.typeDescriptorCache.get(cacheKey); TypeDescriptor typeDescriptor = this.typeDescriptorCache.get(cacheKey);
if (typeDescriptor == null) { if (typeDescriptor == null) {
// attempt to populate the cache entry // attempt to populate the cache entry
...@@ -431,7 +432,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor { ...@@ -431,7 +432,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
return this; return this;
} }
CacheKey cacheKey = new CacheKey(type, name); CacheKey cacheKey = new CacheKey(type, name, target instanceof Class);
InvokerPair invocationTarget = this.readerCache.get(cacheKey); InvokerPair invocationTarget = this.readerCache.get(cacheKey);
if (invocationTarget == null || invocationTarget.member instanceof Method) { if (invocationTarget == null || invocationTarget.member instanceof Method) {
...@@ -490,9 +491,12 @@ public class ReflectivePropertyAccessor implements PropertyAccessor { ...@@ -490,9 +491,12 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
private final String name; private final String name;
public CacheKey(Class clazz, String name) { private boolean targetIsClass;
public CacheKey(Class clazz, String name, boolean targetIsClass) {
this.clazz = clazz; this.clazz = clazz;
this.name = name; this.name = name;
this.targetIsClass = targetIsClass;
} }
@Override @Override
...@@ -504,13 +508,23 @@ public class ReflectivePropertyAccessor implements PropertyAccessor { ...@@ -504,13 +508,23 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
return false; return false;
} }
CacheKey otherKey = (CacheKey) other; CacheKey otherKey = (CacheKey) other;
return (this.clazz.equals(otherKey.clazz) && this.name.equals(otherKey.name)); boolean rtn = true;
rtn &= this.clazz.equals(otherKey.clazz);
rtn &= this.name.equals(otherKey.name);
rtn &= this.targetIsClass == otherKey.targetIsClass;
return rtn;
} }
@Override @Override
public int hashCode() { public int hashCode() {
return this.clazz.hashCode() * 29 + this.name.hashCode(); return this.clazz.hashCode() * 29 + this.name.hashCode();
} }
@Override
public String toString() {
return new ToStringCreator(this).append("clazz", this.clazz).append("name",
this.name).append("targetIsClass", this.targetIsClass).toString();
}
} }
......
...@@ -1820,6 +1820,19 @@ public class SpelReproTests extends ExpressionTestCase { ...@@ -1820,6 +1820,19 @@ public class SpelReproTests extends ExpressionTestCase {
assertEquals(XYZ.Z, Array.get(result, 2)); assertEquals(XYZ.Z, Array.get(result, 2));
} }
@Test
public void SPR_10486() throws Exception {
SpelExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
SPR10486 rootObject = new SPR10486();
Expression classNameExpression = parser.parseExpression("class.name");
Expression nameExpression = parser.parseExpression("name");
assertThat(classNameExpression.getValue(context, rootObject),
equalTo((Object) SPR10486.class.getName()));
assertThat(nameExpression.getValue(context, rootObject),
equalTo((Object) "name"));
}
private static enum ABC {A, B, C} private static enum ABC {A, B, C}
...@@ -1887,4 +1900,20 @@ public class SpelReproTests extends ExpressionTestCase { ...@@ -1887,4 +1900,20 @@ public class SpelReproTests extends ExpressionTestCase {
public static class StaticFinalImpl2 extends AbstractStaticFinal { public static class StaticFinalImpl2 extends AbstractStaticFinal {
} }
/**
* The Class TestObject.
*/
public static class SPR10486 {
private String name = "name";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册