今天来讲讲对于Math函数的Spel分析
@Test public void test2() { ExpressionParser parser = new SpelExpressionParser(); String randomPhrase = parser.parseExpression( "random number is #{T(java.lang.Math).round(30d)}", new TemplateParserContext()).getValue(String.class); System.out.println(randomPhrase); }
【结果输出】
random number is 30
为什么会输出这样的结果,我们来分析源码
TemplateAwareExpressionParser.java
@Override public Expression parseExpression(String expressionString, ParserContext context) throws ParseException { if (context == null) { context = NON_TEMPLATE_PARSER_CONTEXT; } if (context.isTemplate()) { return parseTemplate(expressionString, context); } else { return doParseExpression(expressionString, context); } }
在这个方法中,我们传入了TemplateParserContext这个类。我们来看看这个类
public class TemplateParserContext implements ParserContext { private final String expressionPrefix; private final String expressionSuffix; //指定模板前缀为#{ 和后缀 } public TemplateParserContext() { this("#{", "}"); } public TemplateParserContext(String expressionPrefix, String expressionSuffix) { this.expressionPrefix = expressionPrefix; this.expressionSuffix = expressionSuffix; } @Override public final boolean isTemplate() { return true; } @Override public final String getExpressionPrefix() { return this.expressionPrefix; } @Override public final String getExpressionSuffix() { return this.expressionSuffix; } }
TemplateAwareExpressionParser.java
private Expression parseTemplate(String expressionString, ParserContext context) throws ParseException { if (expressionString.length() == 0) { return new LiteralExpression(""); } //解析字符串 Expression[] expressions = parseExpressions(expressionString, context); if (expressions.length == 1) { return expressions[0]; } else { return new CompositeStringExpression(expressionString, expressions); } }
TemplateAwareExpressionParser.java
private Expression[] parseExpressions(String expressionString, ParserContext context) throws ParseException { List<Expression> expressions = new LinkedList<Expression>(); //获取前缀 ${ String prefix = context.getExpressionPrefix(); //获取后缀 } String suffix = context.getExpressionSuffix(); int startIdx = 0; while (startIdx < expressionString.length()) { int prefixIndex = expressionString.indexOf(prefix, startIdx); if (prefixIndex >= startIdx) { // 如果前缀 #{ 不是出现在第一个字符位置 , // 像 random number is #{T(java.lang.Math).round(30d)} if (prefixIndex > startIdx) { //直接返回LiteralExpression对象 expressions.add(createLiteralExpression(context, expressionString.substring(startIdx, prefixIndex))); } //获取 ${ 后的第一个字符的index int afterPrefixIndex = prefixIndex + prefix.length(); //匹配到 ${ 相匹配后缀的 } index, // 也就是说,找到了 ${ ,和 ${ 相匹配的 } index位置 int suffixIndex = skipToCorrectEndSuffix(prefix, 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); //本例子终于得到了字符串 T(java.lang.Math).round(30d) expr = expr.trim(); if (expr.length() == 0) { throw new ParseException(expressionString, prefixIndex, "No expression defined within delimiter '" + prefix + suffix + "' at character " + prefixIndex); } //继续调用doParseExpression方法 expressions.add(doParseExpression(expr, context)); startIdx = suffixIndex + suffix.length(); } else { // no more ${expressions} found in string, add rest as static text expressions.add(createLiteralExpression(context, expressionString.substring(startIdx))); startIdx = expressionString.length(); } } return expressions.toArray(new Expression[expressions.size()]); }
TemplateAwareExpressionParser.java
private int skipToCorrectEndSuffix(String prefix, String suffix, String expressionString, int afterPrefixIndex) throws ParseException { //保留${ 前缀所在位置 int pos = afterPrefixIndex; //被解析字符串长度 int maxlen = expressionString.length(); //看被解析的字符串中和前缀 ${ 相匹配的后缀存在不?如果不存在,则抛出异常 int nextSuffix = expressionString.indexOf(suffix, afterPrefixIndex); if (nextSuffix == -1) { return -1; } //建立堆栈结构,用户保存被解析的字符串 { ,[ ,( 等 Stack<Bracket> stack = new Stack<Bracket>(); while (pos < maxlen) { //如果栈中的数据为空,并且匹配到了后缀 } ,则退出while循环 //只是本例中的后缀为} ,其实前缀后缀可以根据用户的喜好随便定义的 // 如 : $aaaa 为前缀 bbbb为后缀,都是可以的,在Spring中都帮我们实现了 if (isSuffixHere(expressionString, pos, suffix) && stack.isEmpty()) { break; } char ch = expressionString.charAt(pos); switch (ch) { case '{': case '[': case '(': //如果解析到的字符串是 { ,[ ,( 压栈 stack.push(new Bracket(ch, pos)); break; case '}': case ']': case ')': //如果解析到的字符串是 } ,] ,) 出栈,当前栈中没有元素,则抛出异常,说明 括号不对应 if (stack.isEmpty()) { throw new ParseException(expressionString, pos, "Found closing '" + ch + "' at position " + pos + " without an opening '" + Bracket.theOpenBracketFor(ch) + "'"); } Bracket p = stack.pop(); // 出栈,并且判断当前栈中的元素是不是和当前元素相对应,不允许出下 [ ) 或者 { ] ,括号不对等的情况 if (!p.compatibleWithCloseBracket(ch)) { throw new ParseException(expressionString, pos, "Found closing '" + ch + "' at position " + pos + " but most recent opening is '" + p.bracket + "' at position " + p.pos); } break; case '\'': case '"': // 如果是双引号,看当前被解析的字符串中是不是还有双引号,如果有,将pos位置移动到下一个双引号位置, //如果没有双引号了,直接抛出异常 int endLiteral = expressionString.indexOf(ch, pos + 1); if (endLiteral == -1) { throw new ParseException(expressionString, pos, "Found non terminating string literal starting at position " + pos); } pos = endLiteral; break; } pos++; } // 如果栈中元素不为空,抛出异常 if (!stack.isEmpty()) { Bracket p = stack.pop(); throw new ParseException(expressionString, p.pos, "Missing closing '" + Bracket.theCloseBracketFor(p.bracket) + "' for '" + p.bracket + "' at position " + p.pos); } if (!isSuffixHere(expressionString, pos, suffix)) { return -1; } return pos; }
TemplateAwareExpressionParser.java
private boolean isSuffixHere(String expressionString, int pos, String suffix) { int suffixPosition = 0; // 例如当前解析到的字符是 random number is #{T(java.lang.Math).round(30d)} bbbbbbbbbb // 30后面的d ,而后缀是 } 因此,返回false,继续循环,而只有当前字符串定位在 } ,才可能返回true // 为什么要用pos++的操作呢?主要目的是为了解析,当后缀设置不是一个字符串时候, // $aaa xxxx bbb , 设置前缀为 $aaa ,后缀为 bbb,需要解析表达式中的内容时, // 这个时候就需要后缀就需要pos ++ 来全匹配了 for (int i = 0; i < suffix.length() && pos < expressionString.length(); i++) { if (expressionString.charAt(pos++) != suffix.charAt(suffixPosition++)) { return false; } } if (suffixPosition != suffix.length()) { //目前想不到什么情况会进入到这里,如果有想到的,帮我留言一下吧,我来补充一下 return false; } return true; }
通过上面的解析,我们终于又到了我们熟悉的SpelExpressionParser类doParseExpression方法了。
SpelExpressionParser.java
@Override protected SpelExpression doParseExpression(String expressionString, ParserContext context) throws ParseException { //如果在new SpelExpressionParser这个对象时没有传入,则使用默认的SpelParserConfiguration实例作为configuration传入参数 return new InternalSpelExpressionParser(this.configuration).doParseExpression(expressionString, context); }
又来到了我们熟悉的分词,吃掉表达式,封装对象的环节
@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 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 + "'"); } } } }
分词的第一个字符是T ,则进入lexIdentifier方法。
我们再来看看,我们要解析的表达式
T(java.lang.Math).round(30d)
1.因为’T’后面是 ( ,因此得到枚举类型是TokenKind.IDENTIFIER的Token对象。
(对应的是TokenKing.LPAREN
( 后面的java和下面的lang以及Math以及round都是一样的对应IDENTIFIER
. 对应的是TokenKind 为点的Token
右括号对应的是 TokenKind.RPAREN 的枚举类型,如图所示
下面是关于对应数字的分词,数字30d是如何实现的呢?
private void lexNumericLiteral(boolean firstCharIsZero) { boolean isReal = false; int start = this.pos; char ch = this.toProcess[this.pos + 1]; boolean isHex = ch == 'x' || ch == 'X'; //十六进制处理 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; 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; // Now there may or may not be an exponent //如果数字后面是 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 { 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; } if (isReal) { //保存为符点类型的Token pushRealToken(subarray(start, endOfNumber), isFloat, start, endOfNumber); } else { pushIntToken(subarray(start, endOfNumber), false, start, endOfNumber); } } }
private void pushRealToken(char[] data, boolean isFloat, int start, int end) { if (isFloat) { //真符点类型 this.tokens.add(new Token(TokenKind.LITERAL_REAL_FLOAT, data, start, end)); } else { //类 符点 this.tokens.add(new Token(TokenKind.LITERAL_REAL, data, start, end)); } }
到这里我们终于完成了分词,来看看分词结果
最终,我们得到了分词数组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; }
经过一波一波的查找,那我们到了解析T Token的方法
private boolean maybeEatTypeReference() { if (peekToken(TokenKind.IDENTIFIER)) { Token typeName = peekToken(); //如果类型为 T if (!typeName.stringValue().equals("T")) { return false; } //获取T后面的下一个Token Token t = nextToken(); //如果是 ] ,表示的是一个 map 类型 if (peekToken(TokenKind.RSQUARE)) { // looks like 'T]' (T is map key) push(new PropertyOrFieldReference(false,t.data,toPos(t))); return true; } //吃掉右括号,Spring Spel语法就是这样的 eatToken(TokenKind.LPAREN); // SpelNodeImpl node = eatPossiblyQualifiedId(); // dotted qualified id // Are there array dimensions? int dims = 0; while (peekToken(TokenKind.LSQUARE, true)) { eatToken(TokenKind.RSQUARE); dims++; } eatToken(TokenKind.RPAREN); //建成TypeReference返回 this.constructedNodes.push(new TypeReference(toPos(typeName),node,dims)); return true; } return false; }
private SpelNodeImpl eatPossiblyQualifiedId() { LinkedList qualifiedIdPieces = new LinkedList(); Token node = peekToken(); //如果结点的类型是 TokenKind.DOT 或者 TokenKind.IDENTIFIER while (isValidQualifiedId(node)) { nextToken(); //节点如果不是点 则以Identifier对象加入到qualifiedIdPieces中 if (node.kind != TokenKind.DOT) { qualifiedIdPieces.add(new Identifier(node.stringValue(),toPos(node))); } //否则继续循环 node = peekToken(); } if (qualifiedIdPieces.isEmpty()) { if (node == null) { raiseInternalException( this.expressionString.length(), SpelMessage.OOD); } raiseInternalException(node.startPos, SpelMessage.NOT_EXPECTED_TOKEN, "qualified ID", node.getKind().toString().toLowerCase()); } int pos = toPos(qualifiedIdPieces.getFirst().getStartPosition(), qualifiedIdPieces.getLast().getEndPosition()); //T之后的括号里分词值保存为QualifiedIdentifier返回 return new QualifiedIdentifier(pos, qualifiedIdPieces.toArray(new SpelNodeImpl[qualifiedIdPieces.size()])); }
最后构建成TypeReference返回
//因为括号后面是点,因此进入括号后面的点表达式 private SpelNodeImpl eatDottedNode() { Token t = nextToken();// it was a '.' or a '?.' boolean nullSafeNavigation = t.kind == TokenKind.SAFE_NAVI; if (maybeEatMethodOrProperty(nullSafeNavigation) || maybeEatFunctionOrVar() || maybeEatProjection(nullSafeNavigation) || maybeEatSelection(nullSafeNavigation)) { return pop(); } if (peekToken() == null) { // unexpectedly ran out of data raiseInternalException(t.startPos, SpelMessage.OOD); } else { raiseInternalException(t.startPos, SpelMessage.UNEXPECTED_DATA_AFTER_DOT, toString(peekToken())); } return null; }
private boolean maybeEatMethodOrProperty(boolean nullSafeNavigation) { //如果Token类型是TokenKind.IDENTIFIER,确实round类型是TokenKind.IDENTIFIER if (peekToken(TokenKind.IDENTIFIER)) { //next Token是方法或者属性 Token methodOrPropertyName = nextToken(); //获取方法参数 SpelNodeImpl[] args = maybeEatMethodArgs(); if (args==null) { // property push(new PropertyOrFieldReference(nullSafeNavigation, methodOrPropertyName.data,toPos(methodOrPropertyName))); return true; } //返回方法引用 push(new MethodReference(nullSafeNavigation, methodOrPropertyName.data, toPos(methodOrPropertyName), args)); // TODO what is the end position for a method reference? the name or the last arg? return true; } return false; }
private SpelNodeImpl[] maybeEatMethodArgs() { if (!peekToken(TokenKind.LPAREN)) { return null; } List<SpelNodeImpl> args = new ArrayList<SpelNodeImpl>(); consumeArguments(args); eatToken(TokenKind.RPAREN); return args.toArray(new SpelNodeImpl[args.size()]); }
private void consumeArguments(List accumulatedArguments) { int pos = peekToken().startPos; Token next; do { // 下一个token , 本例中是 ) nextToken(); // consume ( (first time through) or comma (subsequent times) Token t = peekToken(); if (t == null) { raiseInternalException(pos, SpelMessage.RUN_OUT_OF_ARGUMENTS); } // 是不是右括号 if (t.kind != TokenKind.RPAREN) { // 如果是右括号,继续递归吃掉括号内的表达式 30d accumulatedArguments.add(eatExpression()); } next = peekToken(); } while (next != null && next.kind == TokenKind.COMMA); if (next == null) { raiseInternalException(pos, SpelMessage.RUN_OUT_OF_ARGUMENTS); } }
方法参数30d 最后解析得到RealLiteral对象
因为nodes参数中第一个是TypeReference,第二个参数是MethodReference,因此,返回一个复杂对象,CompoundExpression对象。
在这里,我们终于将表达式吃吃完了,那我们最终得到了什么呢,如下图所示
最终返回CompositeStringExpression对象。终于将表达式吃完了,这里,我们继续来分析值的获取 。
CompositeStringExpression.java
@Override public T getValue(Class expectedResultType) throws EvaluationException { Object value = getValue(); // 类型转换 return ExpressionUtils.convertTypedValue(null, new TypedValue(value), expectedResultType); }
@Override public String getValue() throws EvaluationException { StringBuilder sb = new StringBuilder(); for (Expression expression : this.expressions) { String value = expression.getValue(String.class); if (value != null) { sb.append(value); } } return sb.toString(); }
通过上面可以看出,最终我们random number is #{T(java.lang.Math).round(30d)} 是通过
random number is和(java.lang.Math).round(30d) 表达式各自计算出的结果再通过append拼接在一起的。
random number is对应的是LiteralExpression,调用getValue()方法。
LiteralExpression.java
@Override public String getValue() { //直接返回value值 return this.literalValue; }
我们再来看看CompoundExpression表达式的getValue()方法
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) { //expressionState not null return getValueInternal(expressionState); } else { // configuration not set - does that matter? return getTypedValue(new ExpressionState(new StandardEvaluationContext())); } }
CompoundExpression.java
@Override public TypedValue getValueInternal(ExpressionState state) throws EvaluationException { //获取值引用 ValueRef ref = getValueRef(state); TypedValue result = ref.getValue(); this.exitTypeDescriptor = this.children[this.children.length - 1].exitTypeDescriptor; return result; }
CompoundExpression.java
@Override protected ValueRef getValueRef(ExpressionState state) throws EvaluationException { //如果this只有一个child if (getChildCount() == 1) { return this.children[0].getValueRef(state); } //在本例中,第一个参数是TypeReference,因此下面调用TypeReference的getValueInternal方法 SpelNodeImpl nextNode = this.children[0]; try { TypedValue result = nextNode.getValueInternal(state); int cc = getChildCount(); for (int i = 1; i < cc - 1; i++) { try { state.pushActiveContextObject(result); nextNode = this.children[i]; result = nextNode.getValueInternal(state); } finally { state.popActiveContextObject(); } } try { state.pushActiveContextObject(result); nextNode = this.children[cc-1]; //调用方法引用的getValueRef return nextNode.getValueRef(state); } finally { state.popActiveContextObject(); } } catch (SpelEvaluationException ex) { ex.setPosition(nextNode.getStartPosition()); throw ex; } }
TypeReference.java
@Override public TypedValue getValueInternal(ExpressionState state) throws EvaluationException { // TODO possible optimization here if we cache the discovered type reference, but can we do that? //本例中 第一个child是QualifiedIdentifier ,因此调用QualifiedIdentifier的getValueInternal方法,得到了java.lang.Math对象 String typeName = (String) this.children[0].getValueInternal(state).getValue(); if (!typeName.contains(".") && Character.isLowerCase(typeName.charAt(0))) { TypeCode tc = TypeCode.valueOf(typeName.toUpperCase()); if (tc != TypeCode.OBJECT) { // It is a primitive type Class<?> clazz = makeArrayIfNecessary(tc.getType()); this.exitTypeDescriptor = "Ljava/lang/Class"; this.type = clazz; return new TypedValue(clazz); } } //最终在内部通过反射拿到java.lang.Math类型 Class<?> clazz = state.findType(typeName); //如果是集合类型,则转换成集合类型 clazz = makeArrayIfNecessary(clazz); this.exitTypeDescriptor = "Ljava/lang/Class"; this.type = clazz; return new TypedValue(clazz); }
QualifiedIdentifier.java
@Override public TypedValue getValueInternal(ExpressionState state) throws EvaluationException { //这个方法内部其实很简单,就是将child用逗号隔开拼接起来 if (this.value == null) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < getChildCount(); i++) { Object value = this.children[i].getValueInternal(state).getValue(); if (i > 0 && !value.toString().startsWith("$")) { sb.append("."); } sb.append(value); } this.value = new TypedValue(sb.toString()); } return this.value; }
上面己经完成了QualifiedIdentifier的解析,下面来解析MethodReference
MethodReference.java
@Override protected ValueRef getValueRef(ExpressionState state) throws EvaluationException { Object[] arguments = getArguments(state); if (state.getActiveContextObject().getValue() == null) { throwIfNotNullSafe(getArgumentTypes(arguments)); return ValueRef.NullValueRef.INSTANCE; } return new MethodValueRef(state, arguments); }
MethodReference.java
private Object[] getArguments(ExpressionState state) { Object[] arguments = new Object[getChildCount()]; for (int i = 0; i < arguments.length; i++) { try { state.pushActiveContextObject(state.getScopeRootContextObject()); //获取方法参数的getValueInternal方法, //这里我们是获取30d 的类型,RealLiteral 的getValueInternal方法, //再调用getValue()方法,返回 Double(30) arguments[i] = this.children[i].getValueInternal(state).getValue(); } finally { state.popActiveContextObject(); } } return arguments; }
获取MethodValueRef对象,调用MethodValueRef的getValue()方法
MethodValueRef.java
@Override public TypedValue getValue() { TypedValue result = MethodReference.this.getValueInternal( this.evaluationContext, this.value, this.targetType, this.arguments); updateExitTypeDescriptor(); return result; }
MethodReference.java
private TypedValue getValueInternal(EvaluationContext evaluationContext, Object value, TypeDescriptor targetType, Object[] arguments) { //获取参数类型 List<TypeDescriptor> argumentTypes = getArgumentTypes(arguments); if (value == null) { throwIfNotNullSafe(argumentTypes); return TypedValue.NULL; } //从缓存中获取 MethodExecutor executorToUse = getCachedExecutor(evaluationContext, value, targetType, argumentTypes); if (executorToUse != null) { try { return executorToUse.execute(evaluationContext, value, arguments); } catch (AccessException ex) { throwSimpleExceptionIfPossible(value, ex); this.cachedExecutor = null; } } //获取执行方法 executorToUse = findAccessorForMethod(this.name, argumentTypes, value, evaluationContext); this.cachedExecutor = new CachedMethodExecutor( executorToUse, (value instanceof Class ? (Class<?>) value : null), targetType, argumentTypes); try { return executorToUse.execute(evaluationContext, value, arguments); } catch (AccessException ex) { throwSimpleExceptionIfPossible(value, ex); throw new SpelEvaluationException(getStartPosition(), ex, SpelMessage.EXCEPTION_DURING_METHOD_INVOCATION, this.name, value.getClass().getName(), ex.getMessage()); } }
private List getArgumentTypes(Object... arguments) { List descriptors = new ArrayList(arguments.length); for (Object argument : arguments) { descriptors.add(TypeDescriptor.forObject(argument)); } return Collections.unmodifiableList(descriptors); }
MethodReference.java
private MethodExecutor findAccessorForMethod(String name, List argumentTypes, Object targetObject, EvaluationContext evaluationContext) throws SpelEvaluationException { List methodResolvers = evaluationContext.getMethodResolvers(); if (methodResolvers != null) { for (MethodResolver methodResolver : methodResolvers) { try { MethodExecutor methodExecutor = methodResolver.resolve( evaluationContext, targetObject, name, argumentTypes); if (methodExecutor != null) { return methodExecutor; } } catch (AccessException ex) { throw new SpelEvaluationException(getStartPosition(), ex, SpelMessage.PROBLEM_LOCATING_METHOD, name, targetObject.getClass()); } } } throw new SpelEvaluationException(getStartPosition(), SpelMessage.METHOD_NOT_FOUND, FormatHelper.formatMethodForMessage(name, argumentTypes), FormatHelper.formatClassNameForMessage( targetObject instanceof Class ? ((Class<?>) targetObject) : targetObject.getClass())); }
前面的解析中己经分析了,默认是ReflectiveMethodResolver来调用resolve
@Override public MethodExecutor resolve(EvaluationContext context, Object targetObject, String name, List argumentTypes) throws AccessException { try { TypeConverter typeConverter = context.getTypeConverter(); Class<?> type = (targetObject instanceof Class ? (Class<?>) targetObject : targetObject.getClass()); //获取目标类的所有方法 List methods = new ArrayList(getMethods(type, targetObject)); MethodFilter filter = (this.filters != null ? this.filters.get(type) : null); if (filter != null) { List filtered = filter.filter(methods); methods = (filtered instanceof ArrayList ? filtered : new ArrayList(filtered)); } if (methods.size() > 1) { Collections.sort(methods, new Comparator() { @Override public int compare(Method m1, Method m2) { int m1pl = m1.getParameterTypes().length; int m2pl = m2.getParameterTypes().length; // varargs methods go last if (m1pl == m2pl) { if (!m1.isVarArgs() && m2.isVarArgs()) { return -1; } else if (m1.isVarArgs() && !m2.isVarArgs()) { return 1; } else { return 0; } } return (m1pl < m2pl ? -1 : (m1pl > m2pl ? 1 : 0)); } }); } for (int i = 0; i < methods.size(); i++) { methods.set(i, BridgeMethodResolver.findBridgedMethod(methods.get(i))); } Set methodsToIterate = new LinkedHashSet(methods); Method closeMatch = null; int closeMatchDistance = Integer.MAX_VALUE; Method matchRequiringConversion = null; boolean multipleOptions = false; for (Method method : methodsToIterate) { if (method.getName().equals(name)) { Class<?>[] paramTypes = method.getParameterTypes(); List paramDescriptors = new ArrayList(paramTypes.length); for (int i = 0; i < paramTypes.length; i++) { paramDescriptors.add(new TypeDescriptor(new MethodParameter(method, i))); } ReflectionHelper.ArgumentsMatchInfo matchInfo = null; //如果匹配方法是可变参数,那么我们配置的表达式中的参数个数一定大于等于方法参数的个数 if (method.isVarArgs() && argumentTypes.size() >= (paramTypes.length - 1)) { matchInfo = ReflectionHelper.compareArgumentsVarargs(paramDescriptors, argumentTypes, typeConverter); } //不是可变参数方法 else if (paramTypes.length == argumentTypes.size()) { matchInfo = ReflectionHelper.compareArguments(paramDescriptors, argumentTypes, typeConverter); } if (matchInfo != null) { if (matchInfo.isExactMatch()) { return new ReflectiveMethodExecutor(method); } else if (matchInfo.isCloseMatch()) { if (this.useDistance) { int matchDistance = ReflectionHelper.getTypeDifferenceWeight(paramDescriptors, argumentTypes); if (closeMatch == null || matchDistance < closeMatchDistance) { // This is a better match... closeMatch = method; closeMatchDistance = matchDistance; } } else { // Take this as a close match if there isn't one already if (closeMatch == null) { closeMatch = method; } } } else if (matchInfo.isMatchRequiringConversion()) { if (matchRequiringConversion != null) { multipleOptions = true; } matchRequiringConversion = method; } } } } if (closeMatch != null) { return new ReflectiveMethodExecutor(closeMatch); } else if (matchRequiringConversion != null) { if (multipleOptions) { throw new SpelEvaluationException(SpelMessage.MULTIPLE_POSSIBLE_METHODS, name); } return new ReflectiveMethodExecutor(matchRequiringConversion); } else { return null; } } catch (EvaluationException ex) { throw new AccessException("Failed to resolve method", ex); } }
ReflectionHelper.java
static ArgumentsMatchInfo compareArguments( List expectedArgTypes, List suppliedArgTypes, TypeConverter typeConverter) { Assert.isTrue(expectedArgTypes.size() == suppliedArgTypes.size(), "Expected argument types and supplied argument types should be arrays of same length"); ArgumentsMatchKind match = ArgumentsMatchKind.EXACT; for (int i = 0; i < expectedArgTypes.size() && match != null; i++) { TypeDescriptor suppliedArg = suppliedArgTypes.get(i); TypeDescriptor expectedArg = expectedArgTypes.get(i); //匹配到的方法参数类型,和我们配置方法中的参数类型比对 if (!expectedArg.equals(suppliedArg)) { if (suppliedArg == null) { if (expectedArg.isPrimitive()) { match = null; } } else { //配制方法参数类型isAssignableFrom 匹配到的方法参数类型 // suppliedArg = Double ,expectedArg = double if (suppliedArg.isAssignableTo(expectedArg)) { if (match != ArgumentsMatchKind.REQUIRES_CONVERSION) { match = ArgumentsMatchKind.CLOSE; } } else if (typeConverter.canConvert(suppliedArg, expectedArg)) { match = ArgumentsMatchKind.REQUIRES_CONVERSION; } else { match = null; } } } } return (match != null ? new ArgumentsMatchInfo(match) : null); }
ReflectionHelper.java
public static int getTypeDifferenceWeight(List paramTypes, List argTypes) { //获取类型权重 int result = 0; for (int i = 0; i < paramTypes.size(); i++) { TypeDescriptor paramType = paramTypes.get(i); TypeDescriptor argType = (i < argTypes.size() ? argTypes.get(i) : null); if (argType == null) { if (paramType.isPrimitive()) { return Integer.MAX_VALUE; } } else { Class<?> paramTypeClazz = paramType.getType(); if (!ClassUtils.isAssignable(paramTypeClazz, argType.getType())) { return Integer.MAX_VALUE; } if (paramTypeClazz.isPrimitive()) { paramTypeClazz = Object.class; } Class<?> superClass = argType.getType().getSuperclass(); while (superClass != null) { if (paramTypeClazz.equals(superClass)) { result = result + 2; superClass = null; } else if (ClassUtils.isAssignable(paramTypeClazz, superClass)) { result = result + 2; superClass = superClass.getSuperclass(); } else { superClass = null; } } if (paramTypeClazz.isInterface()) { result = result + 1; } } } return result; }
上面己经获取到了Math类的round方法,下面将通过反射来调用round方法
ReflectiveMethodExecutor.java
@Override public TypedValue execute(EvaluationContext context, Object target, Object... arguments) throws AccessException { try { if (arguments != null) { //将配置方法的类型转化成匹配到的方法类型 this.argumentConversionOccurred = ReflectionHelper.convertArguments(context.getTypeConverter(), arguments, this.method, this.varargsPosition); } if (this.method.isVarArgs()) { arguments = ReflectionHelper.setupArgumentsForVarargsInvocation(this.method.getParameterTypes(), arguments); } ReflectionUtils.makeAccessible(this.method); //反射调用相关的方法 ,得到计算值 30 Object value = this.method.invoke(target, arguments); return new TypedValue(value, new TypeDescriptor(new MethodParameter(this.method, -1)).narrow(value)); } catch (Exception ex) { throw new AccessException("Problem invoking method: " + this.method, ex); } }
接下来将得到值30 的 Long类型转换成 目标类型String类型
到这里,我们终于解析完成
最后得到的值是random number is 30
其实这篇博客和第二篇博客的实现方式十分相似。
1.分词
2.解析
3.获取到目标类相匹配的方法。
4.通过反射调用匹配到的方法
5.类型转换,将方法返回值转换成用户需要的值。
Spel表达式源码解析到这里己经告一段落,如果感兴趣的小伙伴,可以下载我的源码,自己写一个,像我的思路,一步步解析,最后得到Spel的值,希望,你能得到你想要的东西。