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