Class ReflectiveIndexAccessor
- All Implemented Interfaces:
Opcodes
,IndexAccessor
,CompilableIndexAccessor
,TargetedAccessor
IndexAccessor
that uses reflection to read from and optionally write to an indexed structure
of a target object.
The indexed structure can be accessed through a public read-method (when being read) or a public write-method (when being written). The relationship between the read-method and write-method is based on a convention that is applicable for typical implementations of indexed structures. See the example below for details.
ReflectiveIndexAccessor
also implements CompilableIndexAccessor
in order to support compilation to bytecode for read access. Note, however,
that the configured read-method must be invokable via a public class or public
interface for compilation to succeed.
Example
The FruitMap
class (the targetType
) represents a structure
that is indexed via the Color
enum (the indexType
). The name
of the read-method is "getFruit"
, and that method returns a
String
(the indexedValueType
). The name of the write-method
is "setFruit"
, and that method accepts a Color
enum (the
indexType
) and a String
(the indexedValueType
which
must match the return type of the read-method).
A read-only IndexAccessor
for FruitMap
can be created via
new ReflectiveIndexAccessor(FruitMap.class, Color.class, "getFruit")
.
With that accessor registered and a FruitMap
registered as a variable
named #fruitMap
, the SpEL expression #fruitMap[T(example.Color).RED]
will evaluate to "cherry"
.
A read-write IndexAccessor
for FruitMap
can be created via
new ReflectiveIndexAccessor(FruitMap.class, Color.class, "getFruit", "setFruit")
.
With that accessor registered and a FruitMap
registered as a variable
named #fruitMap
, the SpEL expression
#fruitMap[T(example.Color).RED] = 'strawberry'
can be used to change
the fruit mapping for the color red from "cherry"
to "strawberry"
.
package example; public enum Color { RED, ORANGE, YELLOW }
public class FruitMap { private final Map<Color, String> map = new HashMap<>(); public FruitMap() { this.map.put(Color.RED, "cherry"); this.map.put(Color.ORANGE, "orange"); this.map.put(Color.YELLOW, "banana"); } public String getFruit(Color color) { return this.map.get(color); } public void setFruit(Color color, String fruit) { this.map.put(color, fruit); } }
- Since:
- 6.2
- Author:
- Sam Brannen
- See Also:
-
Field Summary
Fields inherited from interface org.springframework.asm.Opcodes
AALOAD, AASTORE, ACC_ABSTRACT, ACC_ANNOTATION, ACC_BRIDGE, ACC_DEPRECATED, ACC_ENUM, ACC_FINAL, ACC_INTERFACE, ACC_MANDATED, ACC_MODULE, ACC_NATIVE, ACC_OPEN, ACC_PRIVATE, ACC_PROTECTED, ACC_PUBLIC, ACC_RECORD, ACC_STATIC, ACC_STATIC_PHASE, ACC_STRICT, ACC_SUPER, ACC_SYNCHRONIZED, ACC_SYNTHETIC, ACC_TRANSIENT, ACC_TRANSITIVE, ACC_VARARGS, ACC_VOLATILE, ACONST_NULL, ALOAD, ANEWARRAY, ARETURN, ARRAYLENGTH, ASM10_EXPERIMENTAL, ASM4, ASM5, ASM6, ASM7, ASM8, ASM9, ASTORE, ATHROW, BALOAD, BASTORE, BIPUSH, CALOAD, CASTORE, CHECKCAST, D2F, D2I, D2L, DADD, DALOAD, DASTORE, DCMPG, DCMPL, DCONST_0, DCONST_1, DDIV, DLOAD, DMUL, DNEG, DOUBLE, DREM, DRETURN, DSTORE, DSUB, DUP, DUP_X1, DUP_X2, DUP2, DUP2_X1, DUP2_X2, F_APPEND, F_CHOP, F_FULL, F_NEW, F_SAME, F_SAME1, F2D, F2I, F2L, FADD, FALOAD, FASTORE, FCMPG, FCMPL, FCONST_0, FCONST_1, FCONST_2, FDIV, FLOAD, FLOAT, FMUL, FNEG, FREM, FRETURN, FSTORE, FSUB, GETFIELD, GETSTATIC, GOTO, H_GETFIELD, H_GETSTATIC, H_INVOKEINTERFACE, H_INVOKESPECIAL, H_INVOKESTATIC, H_INVOKEVIRTUAL, H_NEWINVOKESPECIAL, H_PUTFIELD, H_PUTSTATIC, I2B, I2C, I2D, I2F, I2L, I2S, IADD, IALOAD, IAND, IASTORE, ICONST_0, ICONST_1, ICONST_2, ICONST_3, ICONST_4, ICONST_5, ICONST_M1, IDIV, IF_ACMPEQ, IF_ACMPNE, IF_ICMPEQ, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ICMPLT, IF_ICMPNE, IFEQ, IFGE, IFGT, IFLE, IFLT, IFNE, IFNONNULL, IFNULL, IINC, ILOAD, IMUL, INEG, INSTANCEOF, INTEGER, INVOKEDYNAMIC, INVOKEINTERFACE, INVOKESPECIAL, INVOKESTATIC, INVOKEVIRTUAL, IOR, IREM, IRETURN, ISHL, ISHR, ISTORE, ISUB, IUSHR, IXOR, JSR, L2D, L2F, L2I, LADD, LALOAD, LAND, LASTORE, LCMP, LCONST_0, LCONST_1, LDC, LDIV, LLOAD, LMUL, LNEG, LONG, LOOKUPSWITCH, LOR, LREM, LRETURN, LSHL, LSHR, LSTORE, LSUB, LUSHR, LXOR, MONITORENTER, MONITOREXIT, MULTIANEWARRAY, NEW, NEWARRAY, NOP, NULL, POP, POP2, PUTFIELD, PUTSTATIC, RET, RETURN, SALOAD, SASTORE, SIPUSH, SOURCE_DEPRECATED, SOURCE_MASK, SWAP, T_BOOLEAN, T_BYTE, T_CHAR, T_DOUBLE, T_FLOAT, T_INT, T_LONG, T_SHORT, TABLESWITCH, TOP, UNINITIALIZED_THIS, V_PREVIEW, V1_1, V1_2, V1_3, V1_4, V1_5, V1_6, V1_7, V1_8, V10, V11, V12, V13, V14, V15, V16, V17, V18, V19, V20, V21, V22, V23, V9
-
Constructor Summary
ConstructorDescriptionReflectiveIndexAccessor
(Class<?> targetType, Class<?> indexType, String readMethodName) Construct a newReflectiveIndexAccessor
for read-only access.ReflectiveIndexAccessor
(Class<?> targetType, Class<?> indexType, String readMethodName, String writeMethodName) Construct a newReflectiveIndexAccessor
for read-write access. -
Method Summary
Modifier and TypeMethodDescriptionboolean
canRead
(EvaluationContext context, Object target, Object index) Returntrue
if the suppliedtarget
andindex
can be assigned to thetargetType
andindexType
configured via the constructor.boolean
canWrite
(EvaluationContext context, Object target, Object index) Returntrue
if a write-method has been configured andcanRead(org.springframework.expression.EvaluationContext, java.lang.Object, java.lang.Object)
returnstrue
for the same arguments.void
generateCode
(SpelNode index, MethodVisitor mv, CodeFlow cf) Generate bytecode that performs the operation for reading the index.Class<?>
Get the return type of the configured read-method.Class<?>[]
Return an array containing thetargetType
configured via the constructor.boolean
Determine if thisIndexAccessor
is currently suitable for compilation.read
(EvaluationContext context, Object target, Object index) Invoke the configured read-method via reflection and return the result wrapped in aTypedValue
.void
write
(EvaluationContext context, Object target, Object index, Object newValue) Invoke the configured write-method via reflection.
-
Constructor Details
-
ReflectiveIndexAccessor
Construct a newReflectiveIndexAccessor
for read-only access.See class-level documentation for further details and an example.
- Parameters:
targetType
- the type of indexed structure which serves as the target of index operationsindexType
- the type of index used to read from the indexed structurereadMethodName
- the name of the method used to read from the indexed structure
-
ReflectiveIndexAccessor
public ReflectiveIndexAccessor(Class<?> targetType, Class<?> indexType, String readMethodName, @Nullable String writeMethodName) Construct a newReflectiveIndexAccessor
for read-write access.See class-level documentation for further details and an example.
- Parameters:
targetType
- the type of indexed structure which serves as the target of index operationsindexType
- the type of index used to read from or write to the indexed structurereadMethodName
- the name of the method used to read from the indexed structurewriteMethodName
- the name of the method used to write to the indexed structure, ornull
if writing is not supported
-
-
Method Details
-
getSpecificTargetClasses
Return an array containing thetargetType
configured via the constructor.- Specified by:
getSpecificTargetClasses
in interfaceTargetedAccessor
- Returns:
- an array of classes that this accessor is suitable for
(or
null
or an empty array if a generic accessor)
-
canRead
Returntrue
if the suppliedtarget
andindex
can be assigned to thetargetType
andindexType
configured via the constructor.Considers primitive wrapper classes as assignable to the corresponding primitive types.
- Specified by:
canRead
in interfaceIndexAccessor
- Parameters:
context
- the evaluation context in which the access is being attemptedtarget
- the target object upon which the index is being accessedindex
- the index being accessed- Returns:
true
if this index accessor is able to read the index
-
read
Invoke the configured read-method via reflection and return the result wrapped in aTypedValue
.- Specified by:
read
in interfaceIndexAccessor
- Parameters:
context
- the evaluation context in which the access is being attemptedtarget
- the target object upon which the index is being accessedindex
- the index being accessed- Returns:
- a TypedValue object wrapping the index value read and a type descriptor for the value
-
canWrite
Returntrue
if a write-method has been configured andcanRead(org.springframework.expression.EvaluationContext, java.lang.Object, java.lang.Object)
returnstrue
for the same arguments.- Specified by:
canWrite
in interfaceIndexAccessor
- Parameters:
context
- the evaluation context in which the access is being attemptedtarget
- the target object upon which the index is being accessedindex
- the index being accessed- Returns:
true
if this index accessor is able to write to the index
-
write
public void write(EvaluationContext context, Object target, Object index, @Nullable Object newValue) Invoke the configured write-method via reflection.Should only be invoked if
canWrite(org.springframework.expression.EvaluationContext, java.lang.Object, java.lang.Object)
returnstrue
for the same arguments.- Specified by:
write
in interfaceIndexAccessor
- Parameters:
context
- the evaluation context in which the access is being attemptedtarget
- the target object upon which the index is being accessedindex
- the index being accessednewValue
- the new value for the index
-
isCompilable
public boolean isCompilable()Description copied from interface:CompilableIndexAccessor
Determine if thisIndexAccessor
is currently suitable for compilation.May only be known once the index has been read.
- Specified by:
isCompilable
in interfaceCompilableIndexAccessor
- See Also:
-
getIndexedValueType
Get the return type of the configured read-method.- Specified by:
getIndexedValueType
in interfaceCompilableIndexAccessor
- See Also:
-
generateCode
Description copied from interface:CompilableIndexAccessor
Generate bytecode that performs the operation for reading the index.Bytecode should be generated into the supplied
MethodVisitor
using context information from theCodeFlow
where necessary.The supplied
indexNode
should be used to generate the appropriate bytecode to load the index onto the stack. For example, given the expressionbook.authors[0]
, invokingcodeFlow.generateCodeForArgument(methodVisitor, indexNode, int.class)
will ensure that the index (0
) is available on the stack as a primitiveint
.Will only be invoked if
CompilableIndexAccessor.isCompilable()
returnstrue
.- Specified by:
generateCode
in interfaceCompilableIndexAccessor
- Parameters:
index
- theSpelNode
that represents the index being accessedmv
- the ASMMethodVisitor
into which code should be generatedcf
- the current state of the expression compiler
-