在网上,我看到关于Spring的Spel表达式源码解析相关文章相对较少,因此,通过几天的研究,来写一下关系Spring的Spel表达式源码解析相关的文章,希望对大家有帮助
二话不说,直接上代码
@Test public void test11_math_1() { ExpressionParser parser = new SpelExpressionParser(); int two = parser.parseExpression("(21 + 3) * 4").getValue(Integer.class); // 2 System.out.println(two); }
【结果输出】
96
TemplateAwareExpressionParser.java
@Override public Expression parseExpression(String expressionString) throws ParseException { return parseExpression(expressionString, NON_TEMPLATE_PARSER_CONTEXT); }
而NON_TEMPLATE_PARSER_CONTEXT值是一个ParserContext对象,实现为
TemplateAwareExpressionParser.java
private static final ParserContext NON_TEMPLATE_PARSER_CONTEXT = new ParserContext() { @Override //需要解析的表达式前缀 public String getExpressionPrefix() { return null; } //需要解析的表达式后缀 @Override public String getExpressionSuffix() { return null; } //是不是使用模版 @Override public boolean isTemplate() { return false; } };
TemplateAwareExpressionParser.java
@Override public Expression parseExpression(String expressionString, ParserContext context) throws ParseException { if (context == null) { //设置默认的ParserContext context = NON_TEMPLATE_PARSER_CONTEXT; } //是不是使用模版解析方法,这个方法后面文章再来解析了 if (context.isTemplate()) { return parseTemplate(expressionString, context); } else { return doParseExpression(expressionString, context); } }
SpelExpressionParser.java
@Override protected SpelExpression doParseExpression(String expressionString, ParserContext context) throws ParseException { //如果在new SpelExpressionParser这个对象时没有传入,则使用默认的SpelParserConfiguration实例作为configuration传入参数 return new InternalSpelExpressionParser(this.configuration).doParseExpression(expressionString, context); }
经过上面的层层递进,终于到了核心方法了。
InternalSpelExpressionParser.java
@Override protected SpelExpression doParseExpression(String expressionString, ParserContext context) throws ParseException { try { this.expressionString = expressionString; //表达式分词 Tokenizer tokenizer = new Tokenizer(expressionString); tokenizer.process(); this.tokenStream = tokenizer.getTokens(); this.tokenStreamLength = this.tokenStream.size(); this.tokenStreamPointer = 0; this.constructedNodes.clear(); //将表达式解析成SpelNodeImpl对象,这个方法很重要 SpelNodeImpl ast = eatExpression(); if (moreTokens()) { throw new SpelParseException(peekToken().startPos, SpelMessage.MORE_INPUT, toString(nextToken())); } //解析出来的表达式统一封装返回 return new SpelExpression(expressionString, ast, this.configuration); } catch (InternalParseException ex) { throw ex.getCause(); } }
首先我们来看一下分词是如何实现的
Tokenizer.java
public Tokenizer(String inputData) { //保留原始字符串 this.expressionString = inputData; //将字符串分隔成字符数组 this.toProcess = (inputData + "\0").toCharArray(); //设置字符串最大长度 this.max = this.toProcess.length; //初始化当前分词位置为0 this.pos = 0; process(); }
Tokenizer.java
public void process() { //循环遍历字符数组 while (this.pos < this.max) { char ch = this.toProcess[this.pos]; //如果ch是字母 if (isAlphabetic(ch)) { lexIdentifier(); } else { switch (ch) { case '+': //如果字符是++ if (isTwoCharToken(TokenKind.INC)) { pushPairToken(TokenKind.INC); } else { //如果字符是+ pushCharToken(TokenKind.PLUS); } break; case '_': //如果字符是下划线 lexIdentifier(); break; case '-': //如果字符是-- if (isTwoCharToken(TokenKind.DEC)) { pushPairToken(TokenKind.DEC); } else { //字符是 - pushCharToken(TokenKind.MINUS); } break; case ':': //如果字符是分号 pushCharToken(TokenKind.COLON); break; case '.'://如果字符是点 pushCharToken(TokenKind.DOT); break; case ','://如果字符是逗号 pushCharToken(TokenKind.COMMA); break; case '*'://如果字符是乘 pushCharToken(TokenKind.STAR); break; case '/'://如果字符是除 pushCharToken(TokenKind.DIV); break; case '%'://如果字符是取余 pushCharToken(TokenKind.MOD); break; case '('://如果字符是左小括号 pushCharToken(TokenKind.LPAREN); break; case ')'://如果字符是右小括号 pushCharToken(TokenKind.RPAREN); break; case '['://如果字符是左中括号 pushCharToken(TokenKind.LSQUARE); break; case '#'://如果字符是井号 pushCharToken(TokenKind.HASH); break; case ']'://如果字符是右中括号 pushCharToken(TokenKind.RSQUARE); break; case '{'://如果字符是左大括号 pushCharToken(TokenKind.LCURLY); break; case '}'://如果字符是右大括号 pushCharToken(TokenKind.RCURLY); break; case '@'://如果字符是@符号 pushCharToken(TokenKind.BEAN_REF); break; case '^': //如果字符是 ^[ if (isTwoCharToken(TokenKind.SELECT_FIRST)) { pushPairToken(TokenKind.SELECT_FIRST); } else { //如果字符是 ^ pushCharToken(TokenKind.POWER); } break; case '!': //如果字符是 != if (isTwoCharToken(TokenKind.NE)) { pushPairToken(TokenKind.NE); } //如果字符是 ![ else if (isTwoCharToken(TokenKind.PROJECT)) { pushPairToken(TokenKind.PROJECT); } else { //如果字符是 ! pushCharToken(TokenKind.NOT); } break; case '=': //如果字符是 == if (isTwoCharToken(TokenKind.EQ)) { pushPairToken(TokenKind.EQ); } else { //如果字符是 = pushCharToken(TokenKind.ASSIGN); } break; case '&': //如果字符是& ,但是不是 && 抛出异常 if (!isTwoCharToken(TokenKind.SYMBOLIC_AND)) { throw new InternalParseException(new SpelParseException( this.expressionString, this.pos, SpelMessage.MISSING_CHARACTER, "&")); } pushPairToken(TokenKind.SYMBOLIC_AND); break; case '|': //如果字符是 | ,但是不是 || 抛出异常 if (!isTwoCharToken(TokenKind.SYMBOLIC_OR)) { throw new InternalParseException(new SpelParseException( this.expressionString, this.pos, SpelMessage.MISSING_CHARACTER, "|")); } pushPairToken(TokenKind.SYMBOLIC_OR); break; case '?': //如果字符是 ?[ if (isTwoCharToken(TokenKind.SELECT)) { pushPairToken(TokenKind.SELECT); } //如果字符是 ?: else if (isTwoCharToken(TokenKind.ELVIS)) { pushPairToken(TokenKind.ELVIS); } //如果字符是 ?. else if (isTwoCharToken(TokenKind.SAFE_NAVI)) { pushPairToken(TokenKind.SAFE_NAVI); } //如果字符是 ? else { pushCharToken(TokenKind.QMARK); } break; case '$': //如果字符是 $[ if (isTwoCharToken(TokenKind.SELECT_LAST)) { pushPairToken(TokenKind.SELECT_LAST); } //如果字符是 $ else { lexIdentifier(); } break; case '>': //如果字符是 >= if (isTwoCharToken(TokenKind.GE)) { pushPairToken(TokenKind.GE); } else { //如果字符是 > pushCharToken(TokenKind.GT); } break; case '<': //如果字符是 <= if (isTwoCharToken(TokenKind.LE)) { pushPairToken(TokenKind.LE); } else { //如果字符是 < pushCharToken(TokenKind.LT); } break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': // 数字处理 lexNumericLiteral(ch == '0'); break; case ' ': case '\t': case '\r': case '\n': // 空格,回车,tab 处理 this.pos++; break; case '\'': //单引号处理 lexQuotedStringLiteral(); break; case '"': //双引号处理 lexDoubleQuotedStringLiteral(); break; case 0: //0 处理 this.pos++; // will take us to the end break; case '\\':// 单斜杠处理,抛出异常 throw new InternalParseException( new SpelParseException(this.expressionString, this.pos, SpelMessage.UNEXPECTED_ESCAPE_CHAR)); default: throw new IllegalStateException("Cannot handle (" + Integer.valueOf(ch) + ") '" + ch + "'"); } } } }
首先是左括号的处理,传入枚举类型为 TokenKind 的LPAREN("(")
private void pushCharToken(TokenKind kind) { this.tokens.add(new Token(kind, this.pos, this.pos + 1)); //解析字符串的下标向后移动一位 this.pos++; }
Token对象有四个参数
class Token { //枚举类型,比如说是++ ,-- 等 TokenKind kind; //具体的值,如果是字符串解析就有相应的值,如果是符号解析,这个值为空 String data; //本token的值,从被解析字符串截取的起始位置 int startPos; // index of first character //本token的值,从被解析字符串截取的未位位置 int endPos; // index of char after the last character // + - * / 等符号位直接调用 Token(TokenKind tokenKind, int startPos, int endPos) { this.kind = tokenKind; this.startPos = startPos; this.endPos = endPos; } Token(TokenKind tokenKind, char[] tokenData, int startPos, int endPos) { this(tokenKind, startPos, endPos); this.data = new String(tokenData); } public TokenKind getKind() { return this.kind; } @Override public String toString() { StringBuilder s = new StringBuilder(); s.append("[").append(this.kind.toString()); if (this.kind.hasPayload()) { s.append(":").append(this.data); } s.append("]"); s.append("(").append(this.startPos).append(",").append(this.endPos).append(")"); return s.toString(); } public boolean isIdentifier() { return (this.kind == TokenKind.IDENTIFIER); } public boolean isNumericRelationalOperator() { return (this.kind == TokenKind.GT || this.kind == TokenKind.GE || this.kind == TokenKind.LT || this.kind == TokenKind.LE || this.kind==TokenKind.EQ || this.kind==TokenKind.NE); } public String stringValue() { return this.data; } public Token asInstanceOfToken() { return new Token(TokenKind.INSTANCEOF, this.startPos, this.endPos); } public Token asMatchesToken() { return new Token(TokenKind.MATCHES, this.startPos, this.endPos); } public Token asBetweenToken() { return new Token(TokenKind.BETWEEN, this.startPos, this.endPos); } }
被解析的字符串下标右移,此时this.pos值等于1 ,在"(21 + 3) * 4" 字符串中对应的位置是 2 ,而数字对应的方法是lexNumericLiteral,解析如下
Tokenizer.java
private void lexNumericLiteral(boolean firstCharIsZero) { boolean isReal = false; int start = this.pos; char ch = this.toProcess[this.pos + 1]; // 拿到字符串的下一位,是 x 或者X ,如果是,则是十六进制 boolean isHex = ch == 'x' || ch == 'X'; // 如果首位是0并且是十六进制数 if (firstCharIsZero && isHex) { this.pos = this.pos + 1; do { this.pos++; } while (isHexadecimalDigit(this.toProcess[this.pos])); if (isChar('L', 'l')) { pushHexIntToken(subarray(start + 2, this.pos), true, start, this.pos); this.pos++; } else { pushHexIntToken(subarray(start + 2, this.pos), false, start, this.pos); } return; } //下标不断右移,直到没有数字为止 do { this.pos++; } while (isDigit(this.toProcess[this.pos])); //如果数字后的第一个字符串是 . ch = this.toProcess[this.pos]; if (ch == '.') { isReal = true; int dotpos = this.pos; // carry on consuming digits do { this.pos++; } while (isDigit(this.toProcess[this.pos])); if (this.pos == dotpos + 1) { // the number is something like '3.'. It is really an int but may be // part of something like '3.toString()'. In this case process it as // an int and leave the dot as a separate token. this.pos = dotpos; pushIntToken(subarray(start, this.pos), false, start, this.pos); return; } } //保留下标位置,本次解析字符串的截取 int endOfNumber = this.pos; //如果数字字符串之后的第一个字符是L 或者 l ,表示当前数字是Long类型 if (isChar('L', 'l')) { if (isReal) { // 3.4L - not allowed throw new InternalParseException(new SpelParseException(this.expressionString, start, SpelMessage.REAL_CANNOT_BE_LONG)); } pushIntToken(subarray(start, endOfNumber), true, start, endOfNumber); this.pos++; } //如果是E或者e ,表示科学记数法 else if (isExponentChar(this.toProcess[this.pos])) { isReal = true; // if it wasn't before, it is now this.pos++; char possibleSign = this.toProcess[this.pos]; if (isSign(possibleSign)) { this.pos++; } // exponent digits do { this.pos++; } while (isDigit(this.toProcess[this.pos])); boolean isFloat = false; if (isFloatSuffix(this.toProcess[this.pos])) { isFloat = true; endOfNumber = ++this.pos; } else if (isDoubleSuffix(this.toProcess[this.pos])) { endOfNumber = ++this.pos; } pushRealToken(subarray(start, this.pos), isFloat, start, this.pos); } else { //我们知道 21 后面的字符串是空格,此时ch是空格 ch = this.toProcess[this.pos]; boolean isFloat = false; //是不是符点数 if (isFloatSuffix(ch)) { isReal = true; isFloat = true; endOfNumber = ++this.pos; } //是不是double类型 else if (isDoubleSuffix(ch)) { isReal = true; endOfNumber = ++this.pos; } //如果数字是符点类型或者double类型 if (isReal) { pushRealToken(subarray(start, endOfNumber), isFloat, start, endOfNumber); } else { //是整形或者Long类型 pushIntToken(subarray(start, endOfNumber), false, start, endOfNumber); } } }
从断点来看,最后进入了pushIntToken方法,那么我们来总结一下这个方法,方法进入先判断当前数字是不是十六进制数,如果不是,则解析当前数字后的所有的数字 ,如 (21 + 3) * 4 ,如果第一次读取到2,则读尽与2相邻的所有的数字,最后读取到1后面的空格,再通过空格与F 或者 L 或者E 或者D 之类的相比对,看是不是符点类型或者Long类型或者科学计数法或者Double类型,如果都不是,则直接创建Token对象,并保存到 tokens 数组中。如下图所示
我们完成了21的解析,21后面的字符串是空格,因此,代码继续让解析下标右移。
空格后面是+号,因此,继续调用pushCharToken方法,保存Token对象
加号后面是空格,解析下标继续右移
加号后面是 3 ,代码如图所示。
以同样的方法,我们解析出字符串中所有的字符,并将他们封装成一个Token集合,保存到Tokenizer的tokens属性中。
通过分词器,我们得到了一个Token集合。我们再来看看eatExpression方法,从语义上看这个方法是【吃掉表达式】的意思,那是如何吃掉表达式的呢?在进入方法之前,我们来了解一下吃掉表达式中几个常用方法。
InternalSpelExpressionParser.java
moreTokens
private boolean moreTokens() { return this.tokenStreamPointer<this.tokenStream.size(); }
this.tokenStreamPointer表示当前Token集合下标
this.tokenStream.size() 表示当前Token集合长度。
因为在吃掉表达式的方法中,每解析掉一个或者多个Token时候,集合下标增加。当moreTokens返回false时,解析结束。
peekToken
private Token peekToken() { if (this.tokenStreamPointer >= this.tokenStreamLength) { return null; } return this.tokenStream.get(this.tokenStreamPointer); }
不难看出,返回当前下标对应的Token
带参数peekToken
private boolean peekToken(TokenKind possible1,TokenKind possible2) { if (!moreTokens()) { return false; } Token t = peekToken(); return (t.kind == possible1 || t.kind == possible2); }
当前Token的类型是不是传入的类型,如果当前Token是+ ,传入的类型也是+ ,返回true 。否则,false。
nextToken
private Token nextToken() { if (this.tokenStreamPointer >= this.tokenStreamLength) { return null; } return this.tokenStream.get(this.tokenStreamPointer++); }
从这个方法中不难看出,nextToken 主要目标是返回当前下标的Token,并且让下标+1 ,那么下次再调用peekToken()方法或者nextToken()方法时,将返回当前下标对应的Token的下一个Token。
push
private final Stack constructedNodes = new Stack(); private void push(SpelNodeImpl newNode) { this.constructedNodes.push(newNode); }
pop
private SpelNodeImpl pop() { return this.constructedNodes.pop(); }
constructedNodes这是一个栈结构,push方法每次向里面存数据。pop方法每次从栈中弹出数据。
我们了解了上面几个方法后,我们来分析代码
InternalSpelExpressionParser.java
private SpelNodeImpl eatExpression() { SpelNodeImpl expr = eatLogicalOrExpression(); if (moreTokens()) { Token t = peekToken(); 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; }
//吃掉 OR 或者 || private SpelNodeImpl eatLogicalOrExpression() { SpelNodeImpl expr = eatLogicalAndExpression(); while (peekIdentifierToken("or") || peekToken(TokenKind.SYMBOLIC_OR)) { Token t = nextToken(); //consume OR SpelNodeImpl rhExpr =eatLogicalAndExpression(); checkOperands(t, expr, rhExpr); expr = new OpOr(toPos(t), expr, rhExpr); } return expr; }
//吃掉 and 或者 && private SpelNodeImpl eatLogicalAndExpression() { SpelNodeImpl expr = eatRelationalExpression(); while (peekIdentifierToken("and") || peekToken(TokenKind.SYMBOLIC_AND)) { Token t = nextToken(); // consume 'AND' SpelNodeImpl rhExpr = eatRelationalExpression(); checkOperands(t, expr, rhExpr); expr = new OpAnd(toPos(t), expr, rhExpr); } return expr; }
//吃掉 > ,< ,>= ,<=, == 等关系表达式 private SpelNodeImpl eatRelationalExpression() { SpelNodeImpl expr = eatSumExpression(); Token relationalOperatorToken = maybeEatRelationalOperator(); if (relationalOperatorToken != null) { Token t = nextToken(); // 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); 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); return new OperatorBetween(toPos(t), expr, rhExpr); } return expr; }
//吃掉 * , / , % 表达式 private SpelNodeImpl eatSumExpression() { SpelNodeImpl expr = eatProductExpression(); while (peekToken(TokenKind.PLUS, TokenKind.MINUS, TokenKind.INC)) { Token t = nextToken();//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; }
//吃掉 ^ private SpelNodeImpl eatPowerIncDecExpression() { SpelNodeImpl expr = eatUnaryExpression(); if (peekToken(TokenKind.POWER)) { Token t = nextToken(); //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 = nextToken(); //consume INC/DEC if (t.getKind() == TokenKind.INC) { return new OpInc(toPos(t), true, expr); } return new OpDec(toPos(t), true, expr); } return expr; }
//吃掉 + ,- ,! ,++,-- 表达式 private SpelNodeImpl eatUnaryExpression() { if (peekToken(TokenKind.PLUS, TokenKind.MINUS, TokenKind.NOT)) { Token t = nextToken(); SpelNodeImpl expr = eatUnaryExpression(); if (t.kind == TokenKind.NOT) { return new OperatorNot(toPos(t), expr); } if (t.kind == TokenKind.PLUS) { return new OpPlus(toPos(t), expr); } Assert.isTrue(t.kind == TokenKind.MINUS); return new OpMinus(toPos(t), expr); } if (peekToken(TokenKind.INC, TokenKind.DEC)) { Token t = nextToken(); SpelNodeImpl expr = eatUnaryExpression(); if (t.getKind() == TokenKind.INC) { return new OpInc(toPos(t), false, expr); } return new OpDec(toPos(t), false, expr); } //如果以上都不是,那么只能吃掉主表达式了 return eatPrimaryExpression(); }
private SpelNodeImpl eatPrimaryExpression() { List nodes = new ArrayList(); SpelNodeImpl start = eatStartNode(); // always a start node nodes.add(start); //也许吃掉的Token,但不一定能吃得下 while (maybeEatNode()) { nodes.add(pop()); } if (nodes.size() == 1) { return nodes.get(0); } //复杂表达式 return new CompoundExpression(toPos(start.getStartPosition(), nodes.get(nodes.size() - 1).getEndPosition()), nodes.toArray(new SpelNodeImpl[nodes.size()])); }
private SpelNodeImpl eatStartNode() { if (maybeEatLiteral()) { // tokens[0] 是 ( ,返回false return pop(); } else if (maybeEatParenExpression()) {// tokens[0] 是 ( ,则进行处理 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; } }
private boolean maybeEatLiteral() { Token t = peekToken(); //如果token为空,直接返回false if (t == null) { return false; } //如果是int if (t.kind == TokenKind.LITERAL_INT) { push(Literal.getIntLiteral(t.data, toPos(t), 10)); } //如果是long else if (t.kind == TokenKind.LITERAL_LONG) { push(Literal.getLongLiteral(t.data, toPos(t), 10)); } else if (t.kind == TokenKind.LITERAL_HEXINT) { push(Literal.getIntLiteral(t.data, toPos(t), 16)); } //如果是十六进制长整形 else if (t.kind == TokenKind.LITERAL_HEXLONG) { push(Literal.getLongLiteral(t.data, toPos(t), 16)); } else if (t.kind == TokenKind.LITERAL_REAL) { push(Literal.getRealLiteral(t.data, toPos(t), false)); } else if (t.kind == TokenKind.LITERAL_REAL_FLOAT) { push(Literal.getRealLiteral(t.data, toPos(t), true)); } else if (peekIdentifierToken("true")) { push(new BooleanLiteral(t.data, toPos(t), true)); } else if (peekIdentifierToken("false")) { push(new BooleanLiteral(t.data, toPos(t), false)); } else if (t.kind == TokenKind.LITERAL_STRING) { push(new StringLiteral(t.data, toPos(t), t.data)); } else { return false; } nextToken(); return true; }
// token 是 ( ,则进行处理 private boolean maybeEatParenExpression() { // token是左括号 if (peekToken(TokenKind.LPAREN)) { //下标右移 nextToken(); // 递归调用,吃掉括号里的表达式 【 21 + 3 】 SpelNodeImpl expr = eatExpression(); //吃掉右括号 eatToken(TokenKind.RPAREN); push(expr); return true; } else { return false; } }
递归调用eatExpression 方法,21 将被maybeEatLiteral方法解析到。如图所示
将解析到的【21】的 IntLiteral对象返回。
同样的解析方式调用eatProductExpression方法,返回 【3】这个值的 的IntLiteral对象,
最后再构建OpPlus对象
这个对象里的方法,我们进去看看。
最后到了SpelNodeImpl方法。
SpelNodeImpl.java
public SpelNodeImpl(int pos, SpelNodeImpl... operands) { this.pos = pos; // pos combines start and end so can never be zero because tokens cannot be zero length Assert.isTrue(pos != 0, "Pos must not be 0"); if (!ObjectUtils.isEmpty(operands)) { this.children = operands; for (SpelNodeImpl childNode : operands) { childNode.parent = this; } } }
这个方法是什么意思呢?
我们先来看看数据 。
这个代码的意思就是
这个代码的意思其实在我们人类的世界比较简单,比如 父亲张三 有两个儿子小明 ,小林,那么,张三的儿子 就是 小明 和小林,小林的父亲 就是 张三 ,通过张三一定能找到他的两个儿子,通过任意一个儿子都能找到他们的父亲张三。这代码大概是这个意思。
当获取到IntLiteral对象后,下标右移,当前Token是 右括号
因此吃掉括号表达式里的值返回一个OpPlus对象,这个对象有两个child ,一个是 【21】的IntLiteral ,另一个是 【3】的IntLiteral 。
再次通过同样的方法来解析,得到 * 右边的表达式 【4】的IntLiteral 对象。而刚刚解析括号表达式内部的 21 + 3 得到OpPlus对象,通过左边【21 + 3 】OpPlus 和右边【4 】IntLiteral构建OpMultiply【(21 + 3 ) * 4 】并返回,到这里己经完成表达式的全部解析。
最后返回一个OpMultiply对象。
我们做一张图来表示一下。
上面己经完成了分词和表达式的解析。我们来分析一下,对象值的获取。
SpelExpression.java
@Override //用户希望返回的类型 public T getValue(Class expectedResultType) throws EvaluationException { if (this.compiledAst != null) { try { TypedValue contextRoot = evaluationContext == null ? null : evaluationContext.getRootObject(); Object result = this.compiledAst.getValue(contextRoot == null ? null : contextRoot.getValue(), evaluationContext); if (expectedResultType == null) { return (T)result; } else { return ExpressionUtils.convertTypedValue(getEvaluationContext(), new TypedValue(result), expectedResultType); } } catch (Throwable ex) { // If running in mixed mode, revert to interpreted if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) { this.interpretedCount = 0; this.compiledAst = null; } else { // Running in SpelCompilerMode.immediate mode - propagate exception to caller throw new SpelEvaluationException(ex, SpelMessage.EXCEPTION_RUNNING_COMPILED_EXPRESSION); } } } //初始化配置 ExpressionState expressionState = new ExpressionState(getEvaluationContext(), this.configuration); // 获取表达式的类型和值 TypedValue typedResultValue = this.ast.getTypedValue(expressionState); checkCompile(expressionState); // 将值转化成用户期望的类型并返回 return ExpressionUtils.convertTypedValue>(expressionState.getEvaluationContext(), typedResultValue, expectedResultType); }
SpelNodeImpl.java
@Override public final TypedValue getTypedValue(ExpressionState expressionState) throws EvaluationException { if (expressionState != null) { return getValueInternal(expressionState); } else { return getTypedValue(new ExpressionState(new StandardEvaluationContext())); } }
OpMultiply.java
@Override public TypedValue getValueInternal(ExpressionState state) throws EvaluationException { //this.children[0] ,在本例中,返回OpPlus对象 ,调用OpPlus对象的获取值方法,返回24 Object leftOperand = getLeftOperand().getValueInternal(state).getValue(); //this.children[1],在本例中,返回IntLiteral对象,再调用IntLiteral对象的getValue 方法,返回4 Object rightOperand = getRightOperand().getValueInternal(state).getValue(); if (leftOperand instanceof Number && rightOperand instanceof Number) { Number leftNumber = (Number) leftOperand; Number rightNumber = (Number) rightOperand; if (leftNumber instanceof BigDecimal || rightNumber instanceof BigDecimal) { BigDecimal leftBigDecimal = NumberUtils.convertNumberToTargetClass(leftNumber, BigDecimal.class); BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class); return new TypedValue(leftBigDecimal.multiply(rightBigDecimal)); } else if (leftNumber instanceof Double || rightNumber instanceof Double) { this.exitTypeDescriptor = "D"; return new TypedValue(leftNumber.doubleValue() * rightNumber.doubleValue()); } else if (leftNumber instanceof Float || rightNumber instanceof Float) { this.exitTypeDescriptor = "F"; return new TypedValue(leftNumber.floatValue() * rightNumber.floatValue()); } else if (leftNumber instanceof BigInteger || rightNumber instanceof BigInteger) { BigInteger leftBigInteger = NumberUtils.convertNumberToTargetClass(leftNumber, BigInteger.class); BigInteger rightBigInteger = NumberUtils.convertNumberToTargetClass(rightNumber, BigInteger.class); return new TypedValue(leftBigInteger.multiply(rightBigInteger)); } else if (leftNumber instanceof Long || rightNumber instanceof Long) { this.exitTypeDescriptor = "J"; return new TypedValue(leftNumber.longValue() * rightNumber.longValue()); } else if (CodeFlow.isIntegerForNumericOp(leftNumber) || CodeFlow.isIntegerForNumericOp(rightNumber)) { this.exitTypeDescriptor = "I"; return new TypedValue(leftNumber.intValue() * rightNumber.intValue()); } else { // Unknown Number subtypes -> best guess is double multiplication return new TypedValue(leftNumber.doubleValue() * rightNumber.doubleValue()); } } if (leftOperand instanceof String && rightOperand instanceof Integer) { int repeats = (Integer) rightOperand; StringBuilder result = new StringBuilder(); for (int i = 0; i < repeats; i++) { result.append(leftOperand); } return new TypedValue(result.toString()); } return state.operate(Operation.MULTIPLY, leftOperand, rightOperand); }
通过下图我们知道最终返回TypedValue对象,value=24*4 ,OpMultiply的exitTypeDescriptor的值为I 。到这里,我们终于得到了我们想要的值,下面,我们来看看类转换。
我们来看看OpPlus类的getValueInternal方法
OpPlus.java
@Override public TypedValue getValueInternal(ExpressionState state) throws EvaluationException { //拿到左孩子,本例中返回【21】的IntLiteral SpelNodeImpl leftOp = getLeftOperand(); //拿到右孩子, 本例中返回【3】的IntLiteral SpelNodeImpl rightOp = getRightOperand(); if (rightOp == null) { // if only one operand, then this is unary plus Object operandOne = leftOp.getValueInternal(state).getValue(); if (operandOne instanceof Number) { if (operandOne instanceof Double) { this.exitTypeDescriptor = "D"; } else if (operandOne instanceof Float) { this.exitTypeDescriptor = "F"; } else if (operandOne instanceof Long) { this.exitTypeDescriptor = "J"; } else if (operandOne instanceof Integer) { this.exitTypeDescriptor = "I"; } return new TypedValue(operandOne); } return state.operate(Operation.ADD, operandOne, null); } TypedValue operandOneValue = leftOp.getValueInternal(state); // 本例中 获取到 左孩子的值 21 Object leftOperand = operandOneValue.getValue(); TypedValue operandTwoValue = rightOp.getValueInternal(state); //本例中 获取到 右孩子的值 3 Object rightOperand = operandTwoValue.getValue(); //左右孩子的值都是Number类型 if (leftOperand instanceof Number && rightOperand instanceof Number) { Number leftNumber = (Number) leftOperand; Number rightNumber = (Number) rightOperand; if (leftNumber instanceof BigDecimal || rightNumber instanceof BigDecimal) { BigDecimal leftBigDecimal = NumberUtils.convertNumberToTargetClass(leftNumber, BigDecimal.class); BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class); return new TypedValue(leftBigDecimal.add(rightBigDecimal)); } else if (leftNumber instanceof Double || rightNumber instanceof Double) { this.exitTypeDescriptor = "D"; return new TypedValue(leftNumber.doubleValue() + rightNumber.doubleValue()); } else if (leftNumber instanceof Float || rightNumber instanceof Float) { this.exitTypeDescriptor = "F"; return new TypedValue(leftNumber.floatValue() + rightNumber.floatValue()); } else if (leftNumber instanceof BigInteger || rightNumber instanceof BigInteger) { BigInteger leftBigInteger = NumberUtils.convertNumberToTargetClass(leftNumber, BigInteger.class); BigInteger rightBigInteger = NumberUtils.convertNumberToTargetClass(rightNumber, BigInteger.class); return new TypedValue(leftBigInteger.add(rightBigInteger)); } else if (leftNumber instanceof Long || rightNumber instanceof Long) { this.exitTypeDescriptor = "J"; return new TypedValue(leftNumber.longValue() + rightNumber.longValue()); } //如果是Integer,Short ,Byte类型 else if (CodeFlow.isIntegerForNumericOp(leftNumber) || CodeFlow.isIntegerForNumericOp(rightNumber)) { // 设置OpPlus对象的exitTypeDescriptor为I this.exitTypeDescriptor = "I"; //返回TypedValue对象,value=21+3 ,typeDescriptor=null return new TypedValue(leftNumber.intValue() + rightNumber.intValue()); } else { // Unknown Number subtypes -> best guess is double addition return new TypedValue(leftNumber.doubleValue() + rightNumber.doubleValue()); } } if (leftOperand instanceof String && rightOperand instanceof String) { this.exitTypeDescriptor = "Ljava/lang/String"; return new TypedValue((String) leftOperand + rightOperand); } if (leftOperand instanceof String) { return new TypedValue( leftOperand + (rightOperand == null ? "null" : convertTypedValueToString(operandTwoValue, state))); } if (rightOperand instanceof String) { return new TypedValue( (leftOperand == null ? "null" : convertTypedValueToString(operandOneValue, state)) + rightOperand); } return state.operate(Operation.ADD, leftOperand, rightOperand); }
我们再次回到刚刚的代码
细细一看,原来在getTypeDescriptor方法,做了一些改动
直接根据24*4的返回值 获取到了typeDescriptor
下面,我们来继续跟踪代码
需要我们将96这个值转换成Interger ,调用了ExpressionUtils的convertTypedValue方法。
ExpressionUtils.java
public static T convertTypedValue(EvaluationContext context, TypedValue typedValue, Class targetType) { Object value = typedValue.getValue(); //如果用户没有指定,直接强转为泛型类型 if (targetType == null) { return (T) value; } if (context != null) { //获取类型转换器,进行转换,默认是StandardTypeConverter类 return (T) context.getTypeConverter().convertValue( value, typedValue.getTypeDescriptor(), TypeDescriptor.valueOf(targetType)); } if (ClassUtils.isAssignableValue(targetType, value)) { return (T) value; } throw new EvaluationException("Cannot convert value '" + value + "' to type '" + targetType.getName() + "'"); }
StandardTypeConverter.java
@Override public Object convertValue(Object value, TypeDescriptor sourceType, TypeDescriptor targetType) { try { return this.conversionService.convert(value, sourceType, targetType); } catch (ConversionException ex) { throw new SpelEvaluationException( ex, SpelMessage.TYPE_CONVERSION_ERROR, sourceType.toString(), targetType.toString()); } }
在代码中,我们可以看到源类型是Integer类型,目标类型也是Integer类型。
GenericConversionService.java
@Override public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { Assert.notNull(targetType, "targetType to convert to cannot be null"); if (sourceType == null) { Assert.isTrue(source == null, "source must be [null] if sourceType == [null]"); return handleResult(null, targetType, convertNullSource(null, targetType)); } if (source != null && !sourceType.getObjectType().isInstance(source)) { throw new IllegalArgumentException("source to convert from must be an instance of " + sourceType + "; instead it was a " + source.getClass().getName()); } GenericConverter converter = getConverter(sourceType, targetType); if (converter != null) { Object result = ConversionUtils.invokeConverter(converter, source, sourceType, targetType); return handleResult(sourceType, targetType, result); } return handleConverterNotFound(source, sourceType, targetType); }
关系有点诡异,不是说调用默认的DefaultConversionService类嘛,怎么变成了GenericConversionService类了,我们看一下类关系图
原来如此,DefaultConversionService继承了GenericConversionService类。
GenericConversionService.java
protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) { //先从缓存中获取 ConverterCacheKey key = new ConverterCacheKey(sourceType, targetType); GenericConverter converter = this.converterCache.get(key); if (converter != null) { return (converter != NO_MATCH ? converter : null); } //从默认的列表中获取 converter = this.converters.find(sourceType, targetType); if (converter == null) { //默认列表中获取不到,则获取默认的转换器 converter = getDefaultConverter(sourceType, targetType); } if (converter != null) { //保存到缓存中 this.converterCache.put(key, converter); return converter; } this.converterCache.put(key, NO_MATCH); return null; }
那我们看看下图
this.converters中没有从Integer转换成Integer的转换器,那么只能获取默认的转化器了。
我们好奇一下,this.converters这里的默认值是哪里来的呢?
converters这个对象是在GenericConversionService类中定义的。什么时候加进去的呢?我们在addConverter方法中打一个断点,发布是在new DefaultConversionService()方法中加入的默认转换器。
默认转换器竟然是 new NoOpConverter(“NO_OP”) ,那我们继续跟进代码。
ConversionUtils.java
public static Object invokeConverter(GenericConverter converter, Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { try { //根据转换器调用convert方法 return converter.convert(source, sourceType, targetType); } catch (ConversionFailedException ex) { throw ex; } catch (Exception ex) { throw new ConversionFailedException(sourceType, targetType, source, ex); } }
NoOpConverter.java
private static class NoOpConverter implements GenericConverter { private final String name; public NoOpConverter(String name) { this.name = name; } @Override public Set getConvertibleTypes() { return null; } @Override public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { return source; } @Override public String toString() { return this.name; } }
到这里,我们终于明白NoOpConverter类的意义了,就是你原来是什么,就返回什么吧。
到这里,我们终于完成了 (21 + 3 ) * 4 的解析了。那来总结一下吧。
1.分词:将被解析的字符串分成 ( , 21 , + , 3 , ) , * , 4
2.将分词表达式解析成对象树:21 + 3 解析成OpPlus对象 , 4 解析成 IntLiteral对象 ,再一起组成 OpMultiply对象。
3.通过getValue计算值: 先调用左节点 OpPlus的getValueInternal方法计算21 + 3 = 24 并返回 TypedValue 对象,再调用OpMultiply 的getValueInternal 计算出 24 * 4 并返回TypedValue 。
4. 将对象值转换成用户传入类型,如果找不到转换器,直接返回计算结果值。
本人经验,在解读Spring源码时,自己打断点调试。才能得出真理。