Spring - SpEL原理
编译过程
protected SpelExpression doParseExpression(String expressionString, @Nullable ParserContext context)
throws ParseException {
checkExpressionLength(expressionString);
try {
this.expressionString = expressionString;
//词法分析,生成token流
Tokenizer tokenizer = new Tokenizer(expressionString);
this.tokenStream = tokenizer.process();
this.tokenStreamLength = this.tokenStream.size();
this.tokenStreamPointer = 0;
this.constructedNodes.clear();
//语法分析,生成ast
SpelNodeImpl ast = eatExpression();
if (ast == null) {
throw new SpelParseException(this.expressionString, 0, SpelMessage.OOD);
}
Token t = peekToken();
if (t != null) {
throw new SpelParseException(this.expressionString, t.startPos, SpelMessage.MORE_INPUT, toString(nextToken()));
}
//包装ast并返回
return new SpelExpression(expressionString, ast, this.configuration);
}
catch (InternalParseException ex) {
throw ex.getCause();
}
}
语法规则
根据eatExpress方法中的代码(采用自顶向下算法)和注释,可以推出其语法规则
Exp -> LogicOrExp
|LogicOrExp = LogicOrExp
|?:Exp
|?Exp:Exp
LogicOrExp -> LogicAndExp (or LogicAndExp)*
LogicAndExp -> RelationalExp (and RelationalExp)*
RelationalExp -> SumExp (ralationalOp SumExp)*
SumExp -> ProductExp ((+|-) ProductExp)*
ProductExp -> PowerExp ((*|/|%) PowerExp)*
PowerExp -> UnaryExp (^ UnaryExp)? (++|--)
UnaryExp -> (+|-|!|++|--) UnnaryExp
|PrimaryExp
primaryExp -> StartNode ((.|?.)Node)*
StartNode -> LiteralNode
|(Exp)
|TypeRef
|NullRef
|ConstructorRef
|MethodOrProperty
|FunctionOrVal
|BeanRef
|Projection
|Selection
|Indexer
|InlineListOrMap
Node -> DottedNode
| NonDottedNode
DottedNode -> MethodOrProperty
|FunctionOrVal
|Projection
|Selection
NonDottedNode -> Indexer
AST结构
public SpelNodeImpl(int startPos, int endPos, SpelNodeImpl... operands) {
//开始位置
this.startPos = startPos;
//结束位置
this.endPos = endPos;
if (!ObjectUtils.isEmpty(operands)) {
//操作数(子节点)
this.children = operands;
for (SpelNodeImpl operand : operands) {
Assert.notNull(operand, "Operand must not be null");
operand.parent = this;
}
}
}
执行过程
示例代码
parser.parseExpression("list[3]").setValue(demo,"Hello");
SpelExpression.setValue
/** SpelExpression.java */
@Override
public void setValue(@Nullable Object rootObject, @Nullable Object value) throws EvaluationException {
//此时ast为CompounExpression类型,由Property标记(demo中的list属性)和Indexer标记([3]号索引)组成
//同一个CompoundExpression中的子表达式使用同一操作栈
this.ast.setValue(
// ExpressionState 可以视为执行表达式时的操作栈
// 设置Demo对象为rootObject
new ExpressionState(getEvaluationContext(), toTypedValue(rootObject), this.configuration), value);
}
CompounExpression.setValueInternal
/** CompounExpression.java */
@Override
public void setValue(ExpressionState expressionState, @Nullable Object newValue) throws EvaluationException {
setValueInternal(expressionState, () -> new TypedValue(newValue));
}
@Override
public TypedValue setValueInternal(ExpressionState state, Supplier<TypedValue> valueSupplier)
throws EvaluationException {
//TypedValue就是包装一下原来的值并带上类型信息
TypedValue typedValue = valueSupplier.get();
//获取到list中3号索引元素的引用(代码在最后),并设置值
getValueRef(state).setValue(typedValue.getValue());
return typedValue;
}
CompoundExpress.getValueRef
/** CompoundExpress.java */
@Override
protected ValueRef getValueRef(ExpressionState state) throws EvaluationException {
if (getChildCount() == 1) {
return this.children[0].getValueRef(state);
}
SpelNodeImpl nextNode = this.children[0]; //第一个孩子节点,也就是Property节点(list)
try {
//调用Property节点的getValueInternal,获取rootObject上的list属性值
//此处不再往里深究,可以打断点继续调试进去看具体逻辑
TypedValue result = nextNode.getValueInternal(state);
//获取子节点数量,对于索引集合的表达式,子节点数量为2,所以for将不会执行
int cc = getChildCount();
for (int i = 1; i < cc - 1; i++) {
try {
state.pushActiveContextObject(result);
nextNode = this.children[i];
result = nextNode.getValueInternal(state);
}
finally {
state.popActiveContextObject();
}
}
try {
//将刚才获得的list对象压入操作栈
state.pushActiveContextObject(result);
//获取Indexer节点
nextNode = this.children[cc - 1];
//Indexer的getValueRef方法将返回list中某一元素的引用
return nextNode.getValueRef(state);
}
finally {
//操作完成,将list对象弹出操作栈
state.popActiveContextObject();
}
}
catch (SpelEvaluationException ex) {
ex.setPosition(nextNode.getStartPosition());
throw ex;
}
}
/** Indexer.java */
@Override
protected ValueRef getValueRef(ExpressionState state) throws EvaluationException {
//peek操作栈顶
TypedValue context = state.getActiveContextObject();
//target即为要操作的那个list
Object target = context.getValue();
TypeDescriptor targetDescriptor = context.getTypeDescriptor();
TypedValue indexValue;
Object index;
// This first part of the if clause prevents a 'double dereference' of the property (SPR-5847)
if (target instanceof Map && (this.children[0] instanceof PropertyOrFieldReference reference)) {
index = reference.getName();
indexValue = new TypedValue(index);
}
else {
//正常情况代码会执行到此处,并且获取索引值
try {
//将rootObject压入操作栈,适用于target是map且map的索引不是一个字面量,而是一个rootObject中的对象
//此处示例代码是list且索引是字面量,所以此处压栈无用
state.pushActiveContextObject(state.getRootContextObject());
indexValue = this.children[0].getValueInternal(state);
index = indexValue.getValue(); // index 为 3
Assert.state(index != null, "No index");
}
finally {
//弹出rootObject
state.popActiveContextObject();
}
}
// Raise a proper exception in case of a null target
if (target == null) {
throw new SpelEvaluationException(getStartPosition(), SpelMessage.CANNOT_INDEX_INTO_NULL_VALUE);
}
// At this point, we need a TypeDescriptor for a non-null target object
Assert.state(targetDescriptor != null, "No type descriptor");
// 返回map中某一元素的引用
if (target instanceof Map<?, ?> map) {
Object key = index;
if (targetDescriptor.getMapKeyTypeDescriptor() != null) {
key = state.convertValue(key, targetDescriptor.getMapKeyTypeDescriptor());
}
this.indexedType = IndexedType.MAP;
return new MapIndexingValueRef(state.getTypeConverter(), map, key, targetDescriptor);
}
// 返回数组,集合,String中的某一元素的引用
if (target.getClass().isArray() || target instanceof Collection || target instanceof String) {
int idx = (Integer) state.convertValue(index, TypeDescriptor.valueOf(Integer.class));
if (target.getClass().isArray()) {
this.indexedType = IndexedType.ARRAY;
return new ArrayIndexingValueRef(state.getTypeConverter(), target, idx, targetDescriptor);
}
else if (target instanceof Collection<?> collection) {
if (target instanceof List) {
this.indexedType = IndexedType.LIST;
}
//运行此处,返回集合中某一元素的引用
return new CollectionIndexingValueRef(collection, idx, targetDescriptor,
state.getTypeConverter(), state.getConfiguration().isAutoGrowCollections(),
state.getConfiguration().getMaximumAutoGrowSize());
}
else {
this.indexedType = IndexedType.STRING;
return new StringIndexingLValue((String) target, idx, targetDescriptor);
}
}
// Try and treat the index value as a property of the context object
// TODO: could call the conversion service to convert the value to a String
TypeDescriptor valueType = indexValue.getTypeDescriptor();
if (valueType != null && String.class == valueType.getType()) {
this.indexedType = IndexedType.OBJECT;
return new PropertyIndexingValueRef(
target, (String) index, state.getEvaluationContext(), targetDescriptor);
}
throw new SpelEvaluationException(
getStartPosition(), SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE, targetDescriptor);
}
CollectionIndexingValueRef.setValue
@Override
public void setValue(@Nullable Object newValue) {
growCollectionIfNecessary();
if (this.collection instanceof List list) {
if (this.collectionEntryDescriptor.getElementTypeDescriptor() != null) {
newValue = this.typeConverter.convertValue(newValue, TypeDescriptor.forObject(newValue),
this.collectionEntryDescriptor.getElementTypeDescriptor());
}
//设置新值
list.set(this.index, newValue);
}
else {
throw new SpelEvaluationException(getStartPosition(), SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE,
this.collectionEntryDescriptor.toString());
}
}