在spring中@Value实现原理解析(一)——获取环境变量文章中阐述了@Value
的方式注入环境变量的值,这篇文章我们将主要介绍spring如何通过@Value
注解实现表达式的计算,以及实现类型转换的。
首先我们在开始源码解读之前,还是首先看一下具体Demo了解一下@Value
的另一种使用方式。
DEMO
@Slf4j @Service public class ValueAnnotationCollectionService { @Value("#{'${annotation.strings}'.split(',')}") private List<String> items; @PostConstruct public void init() { log.info("转换后的集合结果为: {}", Arrays.toString(items.toArray(new String[0]))); } }
在这个是例子中,我们主要使用@Value
实现了两个功能:
- 从环境变量中读取变量:
annotation.strings
的值 - 将获取到的值,按照
","
分割,并将最终结果转换为List
集合
在spring中@Value实现原理解析(一)——获取环境变量中我们介绍了spring是通过什么样的方式从环境变量中获取需要的值, 因此在这里我们就不再赘述,有兴趣的小伙伴可以先查看之前文章。
SpEL表达式解析
在spring中,配合@Value
的方式使用SpEL表达式时,固定的使用方式需要"#{}
"来表达,当我们在使用"#{}
"时,就意味着spring在解析时,需要通过SpEL
表达式来解析我们的操作意图。因此我们来看看,在Spring中是通过什么样的方式来实现SpEL表达式的。
DefaultListableBeanFactory
在之前的章节中,介绍了最终的@Value
解析是放在了DefaultListableBeanFactory#doResolveDependency()
方法中实现的,因此我们查看一下相关的代码:
doResolveDependency()
Class<?> type = descriptor.getDependencyType(); Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor); if (value != null) { if (value instanceof String) { // 从环境变量中获取变量的具体值,也是解析${}语法的地方 String strVal = resolveEmbeddedValue((String) value); BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null); // 解析SpEL表达式 value = evaluateBeanDefinitionString(strVal, bd); } // 获取类型转换器 TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter()); try { // 判断是否需要类型转换器,如果需要,则执行类型转换,并返回 return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor()); } catch (UnsupportedOperationException ex) { // A custom TypeConverter which does not support TypeDescriptor resolution... return (descriptor.getField() != null ? converter.convertIfNecessary(value, type, descriptor.getField()) : converter.convertIfNecessary(value, type, descriptor.getMethodParameter())); } }
因此在上面方法中,我们可以看到,所有SpEL表达式的解析都是在"${}
"语法解析完成后进行的。
evaluateBeanDefinitionString()
protected Object evaluateBeanDefinitionString(@Nullable String value, @Nullable BeanDefinition beanDefinition) { // 判断是否包含了BeanExpressionResolver, 如果不包含,则放弃解析 if (this.beanExpressionResolver == null) { return value; } Scope scope = null; if (beanDefinition != null) { String scopeName = beanDefinition.getScope(); if (scopeName != null) { scope = getRegisteredScope(scopeName); } } // 执行SpEL表达式解析 return this.beanExpressionResolver.evaluate(value, new BeanExpressionContext(this, scope)); }
真正执行解析实现的,则是通过BeanExpressionResolver
来实现的,通过查看代码,在Spring
中只有一个该接口的实现,则是StandardBeanExpressionResolver
类型,因此我们只需要关注该类即可。
StandardBeanExpressionResolver
evalute()
public Object evaluate(@Nullable String value, BeanExpressionContext evalContext) throws BeansException { // 判断解析的值,是否为空,为空则直接返回 if (!StringUtils.hasLength(value)) { return value; } try { // 从缓存中获取,判断当前值是否已经缓存,如果缓存,则跳过解析步骤 Expression expr = this.expressionCache.get(value); if (expr == null) { // 通过表达式解析器将值解析为表达式 expr = this.expressionParser.parseExpression(value, this.beanExpressionParserContext); // 缓存表达式结果值 this.expressionCache.put(value, expr); } // 根据绑定上下文获取标准计算上下文 // 这个地方的缓存是生效的,虽然BeanExpressionContext每次通过new的方式创建 // 但是该类的hashCode()都是获取的BeanFactory的hashCode()值,因此在同一个容器中,该hashCode()的值是不会变化的 // 通过equals方法也是判断scope与beanFactory是否相等,因此这里的缓存是针对整个容器实现的。 StandardEvaluationContext sec = this.evaluationCache.get(evalContext); if (sec == null) { // 创建标准计算上下文 sec = new StandardEvaluationContext(evalContext); sec.addPropertyAccessor(new BeanExpressionContextAccessor()); sec.addPropertyAccessor(new BeanFactoryAccessor()); sec.addPropertyAccessor(new MapAccessor()); sec.addPropertyAccessor(new EnvironmentAccessor()); sec.setBeanResolver(new BeanFactoryResolver(evalContext.getBeanFactory())); sec.setTypeLocator(new StandardTypeLocator(evalContext.getBeanFactory().getBeanClassLoader())); ConversionService conversionService = evalContext.getBeanFactory().getConversionService(); if (conversionService != null) { sec.setTypeConverter(new StandardTypeConverter(conversionService)); } customizeEvaluationContext(sec); this.evaluationCache.put(evalContext, sec); } // 从expression中获取计算结果 return expr.getValue(sec); } catch (Throwable ex) { throw new BeanExpressionException("Expression parsing failed", ex); } }
SpelExpressionParser
该类的作用,则是将value的值解析成为Expression
表达式。为了更好的了解ExpressionParser
的设计,下图为类结构:
parseExpression()
该方法是将value的值解析为Expression
的入口,具体源码如下:
public Expression parseExpression(String expressionString, @Nullable ParserContext context) throws ParseException { // 当context不为空并且isTemplate为true时,按照模板的方式解析 if (context != null && context.isTemplate()) { return parseTemplate(expressionString, context); } else { // 否则按照InternalSpelExpressionParser解析 return doParseExpression(expressionString, context); } }
parseTemplate
在上面的实现中,因为Context
申明方法为匿名内部类的声明方式,具体代码如下:
private final ParserContext beanExpressionParserContext = new ParserContext() { @Override public boolean isTemplate() { return true; } @Override public String getExpressionPrefix() { return expressionPrefix; } @Override public String getExpressionSuffix() { return expressionSuffix; } };
因此在这里isTemplate
始终为true
。因此我们查看parseTemplate
方法源码如下:
private Expression parseTemplate(String expressionString, ParserContext context) throws ParseException { // 当value为空时,使用LiteralExpression表达式 if (expressionString.isEmpty()) { return new LiteralExpression(""); } // 解析Expression Expression[] expressions = parseExpressions(expressionString, context); if (expressions.length == 1) { return expressions[0]; } else { return new CompositeStringExpression(expressionString, expressions); } }
parseExpressions()
该方法会对value
中的表达式进行解析,并返回多个Expression
列表,具体源码如下:
/** * Helper that parses given expression string using the configured parser. The * expression string can contain any number of expressions all contained in "${...}" * markers. For instance: "foo${expr0}bar${expr1}". The static pieces of text will * also be returned as Expressions that just return that static piece of text. As a * result, evaluating all returned expressions and concatenating the results produces * the complete evaluated string. Unwrapping is only done of the outermost delimiters * found, so the string 'hello ${foo${abc}}' would break into the pieces 'hello ' and * 'foo${abc}'. This means that expression languages that used ${..} as part of their * functionality are supported without any problem. The parsing is aware of the * structure of an embedded expression. It assumes that parentheses '(', square * brackets '[' and curly brackets '}' must be in pairs within the expression unless * they are within a string literal and a string literal starts and terminates with a * single quote '. * @param expressionString the expression string * @return the parsed expressions * @throws ParseException when the expressions cannot be parsed */ private Expression[] parseExpressions(String expressionString, ParserContext context) throws ParseException { // 解析完成表达式列表 List<Expression> expressions = new ArrayList<>(); // 表达式前缀, 默认为"#{" String prefix = context.getExpressionPrefix(); // 表达式后缀, 默认为"}" String suffix = context.getExpressionSuffix(); int startIdx = 0; while (startIdx < expressionString.length()) { // 从startIdx位置开始,判断value中是否包含了前缀"#{" int prefixIndex = expressionString.indexOf(prefix, startIdx); // 找到前缀"#{" if (prefixIndex >= startIdx) { // an inner expression was found - this is a composite // 这里需要生成一个LiteralExpression,因为在找到"#{"之前的所有字符,其实都不需要解析 // 应该作为常量原样的输出 if (prefixIndex > startIdx) { expressions.add(new LiteralExpression(expressionString.substring(startIdx, prefixIndex))); } int afterPrefixIndex = prefixIndex + prefix.length(); // 找到第一个匹配后缀"}"位置,并返回对应索引 int suffixIndex = skipToCorrectEndSuffix(suffix, expressionString, afterPrefixIndex); // 当后缀位置定位失败时,则抛出异常 if (suffixIndex == -1) { throw new ParseException(expressionString, prefixIndex, "No ending suffix '" + suffix + "' for expression starting at character " + prefixIndex + ": " + expressionString.substring(prefixIndex)); } // 如果后缀位置与开始查找位置相同,则说明表达式中没有值 if (suffixIndex == afterPrefixIndex) { throw new ParseException(expressionString, prefixIndex, "No expression defined within delimiter '" + prefix + suffix + "' at character " + prefixIndex); } // 获取开始"#{"与结束"}"中的表达式字符串 String expr = expressionString.substring(prefixIndex + prefix.length(), suffixIndex); expr = expr.trim(); if (expr.isEmpty()) { throw new ParseException(expressionString, prefixIndex, "No expression defined within delimiter '" + prefix + suffix + "' at character " + prefixIndex); } // 对表达式进行解析 expressions.add(doParseExpression(expr, context)); // 冲虚查找下一个表达式 startIdx = suffixIndex + suffix.length(); } else { // no more ${expressions} found in string, add rest as static text // 没有更多的表达式信息, 则返回原样输出即可 expressions.add(new LiteralExpression(expressionString.substring(startIdx))); startIdx = expressionString.length(); } } return expressions.toArray(new Expression[0]); }
doParseExpression()
还记得最开始判断parseContext
与isTemplate
代码入口吗?如果不满足条件时,就直接调用这个方法。而通过查看源码我们可以得知,这个放在在父类中为抽象方法,需要子类来实现,定义如下:
@Override protected SpelExpression doParseExpression(String expressionString, @Nullable ParserContext context) throws ParseException { return new InternalSpelExpressionParser(this.configuration).doParseExpression(expressionString, context); }
这里可以看出,当value
中存在需要处理的表达式时,最终都是通过InternalSpelExpressionParser
类进行处理,因此最终还是需要关注另外一个类.
InternalSpelExpressionParser
doParseExpression()
protected SpelExpression doParseExpression(String expressionString, @Nullable ParserContext context) throws ParseException { try { // 表达式字符串 this.expressionString = expressionString; // 生成tokenizer Tokenizer tokenizer = new Tokenizer(expressionString); // 处理表达式字符串信息, 会根据匹配规则生成Token列表,具体可以看下代码 this.tokenStream = tokenizer.process(); // token长度 this.tokenStreamLength = this.tokenStream.size(); this.tokenStreamPointer = 0; this.constructedNodes.clear(); // 处理token信息 SpelNodeImpl ast = eatExpression(); Assert.state(ast != null, "No node"); Token t = peekToken(); if (t != null) { throw new SpelParseException(t.startPos, SpelMessage.MORE_INPUT, toString(nextToken())); } Assert.isTrue(this.constructedNodes.isEmpty(), "At least one node expected"); // 返回SpelExpression对象 return new SpelExpression(expressionString, ast, this.configuration); } catch (InternalParseException ex) { throw ex.getCause(); } }
eatExpression()
在该方法中能够明确的感知到对于expression
分了不同的层次进行处理,这里我的理解是, 在SpEL
表达式中,本身运算要遵循计算优先级,因此在处理上有很多差异。
// expression // : logicalOrExpression // ( (ASSIGN^ logicalOrExpression) // | (DEFAULT^ logicalOrExpression) // | (QMARK^ expression COLON! expression) // | (ELVIS^ expression))?; @Nullable private SpelNodeImpl eatExpression() { SpelNodeImpl expr = eatLogicalOrExpression(); // 当还包含了后续节点时,则继续判断 Token t = peekToken(); if (t != null) { if (t.kind == TokenKind.ASSIGN) { // a=b if (expr == null) { expr = new NullLiteral(toPos(t.startPos - 1, t.endPos - 1)); } nextToken(); SpelNodeImpl assignedValue = eatLogicalOrExpression(); return new Assign(toPos(t), expr, assignedValue); } if (t.kind == TokenKind.ELVIS) { // a?:b (a if it isn't null, otherwise b) if (expr == null) { expr = new NullLiteral(toPos(t.startPos - 1, t.endPos - 2)); } nextToken(); // elvis has left the building SpelNodeImpl valueIfNull = eatExpression(); if (valueIfNull == null) { valueIfNull = new NullLiteral(toPos(t.startPos + 1, t.endPos + 1)); } return new Elvis(toPos(t), expr, valueIfNull); } if (t.kind == TokenKind.QMARK) { // a?b:c if (expr == null) { expr = new NullLiteral(toPos(t.startPos - 1, t.endPos - 1)); } nextToken(); SpelNodeImpl ifTrueExprValue = eatExpression(); eatToken(TokenKind.COLON); SpelNodeImpl ifFalseExprValue = eatExpression(); return new Ternary(toPos(t), expr, ifTrueExprValue, ifFalseExprValue); } } return expr; }
eatLogicalOrExpression()
private SpelNodeImpl eatLogicalOrExpression() { SpelNodeImpl expr = eatLogicalAndExpression(); // 该处用于逻辑运算,包含了 or 与 || while (peekIdentifierToken("or") || peekToken(TokenKind.SYMBOLIC_OR)) { Token t = takeToken(); //consume OR SpelNodeImpl rhExpr = eatLogicalAndExpression(); checkOperands(t, expr, rhExpr); expr = new OpOr(toPos(t), expr, rhExpr); } return expr; }
eatLogicalAndExpression()
private SpelNodeImpl eatLogicalAndExpression() { SpelNodeImpl expr = eatRelationalExpression(); // 该处用于解析逻辑运算,包含了 and 与 && while (peekIdentifierToken("and") || peekToken(TokenKind.SYMBOLIC_AND)) { Token t = takeToken(); // consume 'AND' SpelNodeImpl rhExpr = eatRelationalExpression(); checkOperands(t, expr, rhExpr); expr = new OpAnd(toPos(t), expr, rhExpr); } return expr; }
eatRelationalExpression()
private SpelNodeImpl eatRelationalExpression() { SpelNodeImpl expr = eatSumExpression(); // 判断关系运算,主要包含以下几种关系: // relationalOperator // : EQUAL | NOT_EQUAL | LESS_THAN | LESS_THAN_OR_EQUAL | GREATER_THAN // | GREATER_THAN_OR_EQUAL | INSTANCEOF | BETWEEN | MATCHES Token relationalOperatorToken = maybeEatRelationalOperator(); if (relationalOperatorToken != null) { Token t = takeToken(); // consume relational operator token SpelNodeImpl rhExpr = eatSumExpression(); checkOperands(t, expr, rhExpr); TokenKind tk = relationalOperatorToken.kind; if (relationalOperatorToken.isNumericRelationalOperator()) { int pos = toPos(t); if (tk == TokenKind.GT) { return new OpGT(pos, expr, rhExpr); } if (tk == TokenKind.LT) { return new OpLT(pos, expr, rhExpr); } if (tk == TokenKind.LE) { return new OpLE(pos, expr, rhExpr); } if (tk == TokenKind.GE) { return new OpGE(pos, expr, rhExpr); } if (tk == TokenKind.EQ) { return new OpEQ(pos, expr, rhExpr); } Assert.isTrue(tk == TokenKind.NE, "Not-equals token expected"); return new OpNE(pos, expr, rhExpr); } if (tk == TokenKind.INSTANCEOF) { return new OperatorInstanceof(toPos(t), expr, rhExpr); } if (tk == TokenKind.MATCHES) { return new OperatorMatches(toPos(t), expr, rhExpr); } Assert.isTrue(tk == TokenKind.BETWEEN, "Between token expected"); return new OperatorBetween(toPos(t), expr, rhExpr); } return expr; }
eatSumExpression()
private SpelNodeImpl eatSumExpression() { SpelNodeImpl expr = eatProductExpression(); // 判断是否为 +、-、++运算 while (peekToken(TokenKind.PLUS, TokenKind.MINUS, TokenKind.INC)) { Token t = takeToken(); //consume PLUS or MINUS or INC SpelNodeImpl rhExpr = eatProductExpression(); checkRightOperand(t, rhExpr); if (t.kind == TokenKind.PLUS) { expr = new OpPlus(toPos(t), expr, rhExpr); } else if (t.kind == TokenKind.MINUS) { expr = new OpMinus(toPos(t), expr, rhExpr); } } return expr; }
eatProductExpression()
private SpelNodeImpl eatProductExpression() { SpelNodeImpl expr = eatPowerIncDecExpression(); //判断是否为*、/、%运算, 并返回对应的Node节点信息 while (peekToken(TokenKind.STAR, TokenKind.DIV, TokenKind.MOD)) { Token t = takeToken(); // consume STAR/DIV/MOD SpelNodeImpl rhExpr = eatPowerIncDecExpression(); checkOperands(t, expr, rhExpr); if (t.kind == TokenKind.STAR) { expr = new OpMultiply(toPos(t), expr, rhExpr); } else if (t.kind == TokenKind.DIV) { expr = new OpDivide(toPos(t), expr, rhExpr); } else { Assert.isTrue(t.kind == TokenKind.MOD, "Mod token expected"); expr = new OpModulus(toPos(t), expr, rhExpr); } } return expr; }
eatPowerIncDecExpression()
private SpelNodeImpl eatPowerIncDecExpression() { // 调用一元操作 SpelNodeImpl expr = eatUnaryExpression(); // 判断是否为平方计算,是则返回OperatorPower操作 if (peekToken(TokenKind.POWER)) { Token t = takeToken(); //consume POWER SpelNodeImpl rhExpr = eatUnaryExpression(); checkRightOperand(t, rhExpr); return new OperatorPower(toPos(t), expr, rhExpr); } // 判断是否为++或者--运算 if (expr != null && peekToken(TokenKind.INC, TokenKind.DEC)) { Token t = takeToken(); //consume INC/DEC if (t.getKind() == TokenKind.INC) { return new OpInc(toPos(t), true, expr); } return new OpDec(toPos(t), true, expr); } return expr; }
eatUnaryExpression()
unaryExpression: (PLUS^ | MINUS^ | BANG^ | INC^ | DEC^) unaryExpression | primaryExpression
; 该方法主要计算一元表达式
private SpelNodeImpl eatUnaryExpression() { // 判断单签token是否为PLUS, MINUX, NOT类型 if (peekToken(TokenKind.PLUS, TokenKind.MINUS, TokenKind.NOT)) { // 获取token Token t = takeToken(); // 继续获取一元表达式 SpelNodeImpl expr = eatUnaryExpression(); Assert.state(expr != null, "No node"); // 判断token的类型是否为: NOT(!) if (t.kind == TokenKind.NOT) { return new OperatorNot(toPos(t), expr); } // 判断Token的类型是否为:PLUS, 如果是,则返回OpPlus if (t.kind == TokenKind.PLUS) { return new OpPlus(toPos(t), expr); } // 否则返回OpMinus Assert.isTrue(t.kind == TokenKind.MINUS, "Minus token expected"); return new OpMinus(toPos(t), expr); } // 判断token的类型是否为INC(++)或者DEC(--) if (peekToken(TokenKind.INC, TokenKind.DEC)) { // 获取当前token Token t = takeToken(); // 获取一元节点 SpelNodeImpl expr = eatUnaryExpression(); // 如果token的类型为:INC, 则返回OpInc if (t.getKind() == TokenKind.INC) { return new OpInc(toPos(t), false, expr); } // 否则返回OpDec return new OpDec(toPos(t), false, expr); } return eatPrimaryExpression(); }
eatPrimaryExpression()
private SpelNodeImpl eatPrimaryExpression() { // 该处主要获取常量Node信息 SpelNodeImpl start = eatStartNode(); // always a start node List<SpelNodeImpl> nodes = null; // 当常量获取完成后,则需要判断后续是否有操作符,主要包括DOT(.)和SAVE_NAVI(?.)两类 SpelNodeImpl node = eatNode(); // 该处主要是为了将常量Node与操作符Node进行组装 while (node != null) { if (nodes == null) { nodes = new ArrayList<>(4); nodes.add(start); } nodes.add(node); node = eatNode(); } if (start == null || nodes == null) { return start; } // 生成CompoundExpression并返回 return new CompoundExpression(toPos(start.getStartPosition(), nodes.get(nodes.size() - 1).getEndPosition()), nodes.toArray(new SpelNodeImpl[0])); }
eatStartNode()
该方法用于解析startNode
信息,主要包含以下信息:
//startNode // : parenExpr | literal // | type // | methodOrProperty // | functionOrVar // | projection // | selection // | firstSelection // | lastSelection // | indexer // | constructor
private SpelNodeImpl eatStartNode() { if (maybeEatLiteral()) { return pop(); } else if (maybeEatParenExpression()) { return pop(); } else if (maybeEatTypeReference() || maybeEatNullReference() || maybeEatConstructorReference() || maybeEatMethodOrProperty(false) || maybeEatFunctionOrVar()) { return pop(); } else if (maybeEatBeanReference()) { return pop(); } else if (maybeEatProjection(false) || maybeEatSelection(false) || maybeEatIndexer()) { return pop(); } else if (maybeEatInlineListOrMap()) { return pop(); } else { return null; } }
maybeEatLiteral()
// literal // : INTEGER_LITERAL // | boolLiteral // | STRING_LITERAL // | HEXADECIMAL_INTEGER_LITERAL // | REAL_LITERAL // | DQ_STRING_LITERAL // | NULL_LITERAL private boolean maybeEatLiteral() { // 获取当前token, 但是游标不会向后移动 Token t = peekToken(); if (t == null) { return false; } // 类型为int时,将token中的值转化为int, 并放入队列中 if (t.kind == TokenKind.LITERAL_INT) { push(Literal.getIntLiteral(t.stringValue(), toPos(t), 10)); } // 类型为long时,将token中的值转化为long, 并放入队列中 else if (t.kind == TokenKind.LITERAL_LONG) { push(Literal.getLongLiteral(t.stringValue(), toPos(t), 10)); } // 类型为16进制整型时,将值转换为16进制,并放入队列中 else if (t.kind == TokenKind.LITERAL_HEXINT) { push(Literal.getIntLiteral(t.stringValue(), toPos(t), 16)); } // 类型为16进制整型时,将值转换为16进制,并放入队列中 else if (t.kind == TokenKind.LITERAL_HEXLONG) { push(Literal.getLongLiteral(t.stringValue(), toPos(t), 16)); } // 当类型为bool类型时,将token的值转换为bool值并放入队列 else if (t.kind == TokenKind.LITERAL_REAL) { push(Literal.getRealLiteral(t.stringValue(), toPos(t), false)); } // 当类型为bool类型时,将token的值转换为bool值并放入队列 else if (t.kind == TokenKind.LITERAL_REAL_FLOAT) { push(Literal.getRealLiteral(t.stringValue(), toPos(t), true)); } // 当类型为字符串true时,将token的值转换为bool值并放入队列 else if (peekIdentifierToken("true")) { push(new BooleanLiteral(t.stringValue(), toPos(t), true)); } // 当类型为字符串false时,将token的值转换为bool值并放入队列 else if (peekIdentifierToken("false")) { push(new BooleanLiteral(t.stringValue(), toPos(t), false)); } // 当类型为字符串类型时,将token的值转换字符串 else if (t.kind == TokenKind.LITERAL_STRING) { push(new StringLiteral(t.stringValue(), toPos(t), t.stringValue())); } else { return false; } // 向后移动游标 nextToken(); return true; }
SpelExpression
public Object getValue(EvaluationContext context) throws EvaluationException { Assert.notNull(context, "EvaluationContext is required"); // 判断当前是否已经包含了编译后的表达式 CompiledExpression compiledAst = this.compiledAst; if (compiledAst != null) { try { // 当前表达式已经编译完成,则可以直接获取值 return compiledAst.getValue(context.getRootObject().getValue(), context); } catch (Throwable ex) { // If running in mixed mode, revert to interpreted if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) { this.compiledAst = null; this.interpretedCount.set(0); } else { // Running in SpelCompilerMode.immediate mode - propagate exception to caller throw new SpelEvaluationException(ex, SpelMessage.EXCEPTION_RUNNING_COMPILED_EXPRESSION); } } } // 当前表达式没有被编译过,则创建ExpressionState对象 ExpressionState expressionState = new ExpressionState(context, this.configuration); // 该处有一个ExpressionState, 该类主要是一个状态存储的类,并且该类会存储中间结果,同时 // 运算的执行也是通过栈的方式存储,因此这里跟我们平时的使用方式差异不大 Object result = this.ast.getValue(expressionState); checkCompile(expressionState); return result; }
由上面Expression
的表达式解析过程可以得知,其实最终获取到是SpelNodeImpl
类型的具体实现类,因此大家感兴趣可以再去多研究一下。
其实在Spring
中,使用SpEL
表达式实现一个运算结果的实现是很复杂的,以上只是我学习的一个思路,如果有帮助到你,请你为文章评论,点赞,感谢!
文章评论