Spring - SpEL原理

161 阅读3分钟

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