spring中@Value注解原理解析——SpEL表达式

13 11 月, 2021 233点热度 0人点赞 0条评论

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的设计,下图为类结构:

spel

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

还记得最开始判断parseContextisTemplate代码入口吗?如果不满足条件时,就直接调用这个方法。而通过查看源码我们可以得知,这个放在在父类中为抽象方法,需要子类来实现,定义如下:

@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表达式实现一个运算结果的实现是很复杂的,以上只是我学习的一个思路,如果有帮助到你,请你为文章评论,点赞,感谢!

 

专注着

一个奋斗在编程路上的小伙

文章评论