Description
This is technically a request to re-open SPR-17480 (JDK 11: Illegal reflective access by org.springframework.util.ReflectionUtils), since the problem has nothing to do with Thymeleaf.
Problem can be reproduced using Java 11.0.1 and Spring Expression 5.1.3.RELEASE with the following code:
public class Test {
public static void main(String[] args) {
String expr = "entry.key";
new SpelExpressionParser().parseExpression(expr).getValue(new Test());
}
public Map.Entry<String, String> getEntry() {
return Map.of("foo", "bar").entrySet().iterator().next();
}
}
Running with --illegal-access=debug
produces the following output:
WARNING: Illegal reflective access by org.springframework.util.ReflectionUtils (file:/C:/Users/Andreas/.gradle/caches/modules-2/files-2.1/org.springframework/spring-core/5.1.3.RELEASE/b1e5325d35bfb27e42d57e9295510cad54ed8fdf/spring-core-5.1.3.RELEASE.jar) to method java.util.KeyValueHolder.getKey()
at org.springframework.util.ReflectionUtils.makeAccessible(ReflectionUtils.java:499)
at org.springframework.expression.spel.support.ReflectivePropertyAccessor$OptimalPropertyAccessor.read(ReflectivePropertyAccessor.java:691)
at org.springframework.expression.spel.ast.PropertyOrFieldReference.readProperty(PropertyOrFieldReference.java:204)
at org.springframework.expression.spel.ast.PropertyOrFieldReference.getValueInternal(PropertyOrFieldReference.java:104)
at org.springframework.expression.spel.ast.PropertyOrFieldReference.access$000(PropertyOrFieldReference.java:51)
at org.springframework.expression.spel.ast.PropertyOrFieldReference$AccessorLValue.getValue(PropertyOrFieldReference.java:406)
at org.springframework.expression.spel.ast.CompoundExpression.getValueInternal(CompoundExpression.java:90)
at org.springframework.expression.spel.ast.SpelNodeImpl.getValue(SpelNodeImpl.java:111)
at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:201)
at test.Test.main(Test.java:10)
The code in SPR-17480 tries to access java.util.HashMap$Node.getKey()
, while the code above tries to access java.util.KeyValueHolder.getKey()
, simply because a different Map
implementation is used. The issue is the same.
The problem is that although the value returned by getEntry()
is an interface with public methods (Map.Entry
), the real object returned is a non-public class implementing the interface. Since the ReflectivePropertyAccessor
logic resolves to the method of the non-public class, not to the method of the interface, ReflectionUtils.makeAccessible(Method method)
ends up calling method.setAccessible(true)
.
Possible solution: Enhance ReflectivePropertyAccessor
to search for the accessor method in public interfaces and public (super-)classes. Default to current logic if method cannot be located in a public type.