之前的博客中解析了(21 + 3 ) * 4 是如何计算来的,今天来看一下字符串的拼接解析。直接上代码。
@Test public void test1() { ExpressionParser parser = new SpelExpressionParser(); Expression exp = parser.parseExpression("'Hello World'.concat('!')"); String message = (String) exp.getValue(); System.out.println(message); }
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(); }
因为很多的方法都和SPEL博客中的一样,那我就直接考呗过来了。
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); } }
被解析字符串的第一个字符是单引号,因此走到如下代码。
Tokenizer.java
private void lexQuotedStringLiteral() { int start = this.pos; boolean terminated = false; //这个while循环的目的就是将分词的下标移动到下一个单引号为止,方便后面截取单引号内的字符串 while (!terminated) { this.pos++; char ch = this.toProcess[this.pos]; if (ch == '\'') { // may not be the end if the char after is also a ' if (this.toProcess[this.pos + 1] == '\'') { this.pos++; // skip over that too, and continue } else { terminated = true; } } if (ch == 0) { throw new InternalParseException(new SpelParseException(this.expressionString, start, SpelMessage.NON_TERMINATING_QUOTED_STRING)); } } this.pos++; //将单引号内的字符串截取出来保存为 枚举类型为TokenKind.LITERAL_STRING的Token this.tokens.add(new Token(TokenKind.LITERAL_STRING, subarray(start, this.pos), start, this.pos)); }
'Hello Word’的下一个字符串是点
调用pushCharToken方法,保存枚举类型为. 的Token
点后面是’c’字符,因此直接调用lexIdentifier 方法
private void lexIdentifier() { int start = this.pos; //只要是数字,字母 ,下划线,以及$ 符号,pos 不断++ do { this.pos++; } while (isIdentifier(this.toProcess[this.pos])); //得到字符数组{c,o,n,c,a,t} char[] subarray = subarray(start, this.pos); //得到字符数组{c,o,n,c,a,t} if ((this.pos - start) == 2 || (this.pos - start) == 3) { String asString = new String(subarray).toUpperCase(); int idx = Arrays.binarySearch(ALTERNATIVE_OPERATOR_NAMES, asString); if (idx >= 0) { pushOneCharOrTwoCharToken(TokenKind.valueOf(asString), start, subarray); return; } } //将字符串保存为枚举类型为TokenKind.IDENTIFIER的Token this.tokens.add(new Token(TokenKind.IDENTIFIER, subarray, start, this.pos)); }
最后将左右小括号分别保存为枚举类型为TokenKind.LPAREN,TokenKind.RPAREN的Token对象,分词结束
最后我们得到分词结果
通过分词器,我们得到了一个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; } }
因为tokens[0]中第一个元素是’Hello World’是枚举类型为TokenKind.LITERAL_STRING的Tokens 因此,封装返回了StringLiteral对象。
private boolean maybeEatLiteral() { Token t = peekToken(); if (t == null) { return false; } if (t.kind == TokenKind.LITERAL_INT) { push(Literal.getIntLiteral(t.data, toPos(t), 10)); } 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; }
Tokens第二个元素是. ,因此进入吃掉点表达式
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) { if (peekToken(TokenKind.IDENTIFIER)) { //下标右移,返回当前Token,可能是方法,也可能是属性 Token methodOrPropertyName = nextToken(); //吃掉方法中的参数 SpelNodeImpl[] args = maybeEatMethodArgs(); if (args==null) { // property push(new PropertyOrFieldReference(nullSafeNavigation, methodOrPropertyName.data,toPos(methodOrPropertyName))); return true; } // methodreference 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() { //如果没有 左括号,则表示当前token是一个属性 if (!peekToken(TokenKind.LPAREN)) { return null; } //如果有左括号,则表示是一个方法,吃掉方法中的参数值 List args = new ArrayList(); 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(); //获取到左括号后的nextToken Token t = peekToken(); if (t == null) { raiseInternalException(pos, SpelMessage.RUN_OUT_OF_ARGUMENTS); } //如果不是右括号,则加入到参数集合中,本例中concat后是 '!' 参数, //因此继续递归调用eatExpression方法,根据上面的逻辑,这里就不再缀述了,'!' //Token 将直接返回一个StringLiteral对象 ,加入到accumulatedArguments集合中。 if (t.kind != TokenKind.RPAREN) { accumulatedArguments.add(eatExpression()); } next = peekToken(); } //如果当前Token是逗号,则继续循环 while (next != null && next.kind == TokenKind.COMMA); if (next == null) { raiseInternalException(pos, SpelMessage.RUN_OUT_OF_ARGUMENTS); } }
返回一个MethodReference对象,将方法参数加入到MethodReference子节点中。到这里吃掉表达式己经解析完成。
在这里,第一步吃掉了’Hello World’ ,在调用mybeEatNode()方法时,在这里吃掉了方法concat 及方法参数 ‘!’ ,返回了MethodReference对象。此时此刻,nodes中有两个参数,则构建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); }
调用CompoundExpression 的getValue方法。
SpelNodeImpl.java
@Override public final Object getValue(ExpressionState expressionState) throws EvaluationException { if (expressionState != null) { //调用自己类的getValueInternal方法 return getValueInternal(expressionState).getValue(); } else { // configuration not set - does that matter? return getValue(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; }
@Override protected ValueRef getValueRef(ExpressionState state) throws EvaluationException { if (getChildCount() == 1) { return this.children[0].getValueRef(state); } //取出第一个元素 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]; return nextNode.getValueRef(state); } finally { state.popActiveContextObject(); } } catch (SpelEvaluationException ex) { // Correct the position for the error before re-throwing ex.setPosition(nextNode.getStartPosition()); throw ex; } }
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); }
当获取方法参数后,我们只看到new MethodValueRef(state, arguments) 并返回,并没有设置value的值和targetType,这两个值是怎样设置进去的呢?带着疑问,我们回头来看代码。
原来是在new MethodValueRef对象的时候,从activeContextObject堆中取出来的。
而这个堆中储存的就是第一个元素IntLiteral对象。
到这里,我们终于得到了ValueRef对象。
本例中继续调用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 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; } } //如果缓存中不存在,则直接根据value值来获取执行器 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()); } }
MethodReference.java
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())); }
首先拿到方法解析器列表,里面又是如何获取的呢?我们跟进代码。
StandardEvaluationContext.java
@Override public List getMethodResolvers() { ensureMethodResolversInitialized(); return this.methodResolvers; }
private void ensureMethodResolversInitialized() { if (this.methodResolvers == null) { initializeMethodResolvers(); } }
private synchronized void initializeMethodResolvers() { if (this.methodResolvers == null) { List defaultResolvers = new ArrayList(); this.reflectiveMethodResolver = new ReflectiveMethodResolver(); defaultResolvers.add(this.reflectiveMethodResolver); this.methodResolvers = defaultResolvers; } }
通过上面代码,我们得到,在List集合初始化为 ReflectiveMethodResolver 对象。
ReflectiveMethodResolver.java
@Override public MethodExecutor resolve(EvaluationContext context, Object targetObject, String name, List argumentTypes) throws AccessException { try { //获取类型转换器,默认是StandardTypeConverter TypeConverter typeConverter = context.getTypeConverter(); Class<?> type = (targetObject instanceof Class ? (Class<?>) targetObject : targetObject.getClass()); //根据目标类型获取该类型所有的方法,targetObject = 'Hello World' ,在这里是获取String类型的所有方法 List methods = new ArrayList(getMethods(type, targetObject)); // If a filter is registered for this type, call it 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++) { //Resolve any bridge methods,如果有桥接方法的处理 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)) { // *sigh* complicated matchInfo = ReflectionHelper.compareArgumentsVarargs(paramDescriptors, argumentTypes, typeConverter); } //方法参数个数和我们传入字符串中方法中参数个数相等 else if (paramTypes.length == argumentTypes.size()) { // Name and parameter number match, check the arguments 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) { //如果没有正确的匹配类型,有多个模糊匹配的类型,则抛出异常 //比如 解析字符串中是String类型,但是反射类中的是Integer类型,和Double类型 //的方法,如果只有其中一个,将走下面方法返回,如果有多个,则抛出异常 throw new SpelEvaluationException(SpelMessage.MULTIPLE_POSSIBLE_METHODS, name); } //模糊匹配得到的类型,比如concat方法需要的是String类型,但是我们被解析的字符串中传递的是Integer类型 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) { 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 { if (suppliedArg.isAssignableTo(expectedArg)) { if (match != ArgumentsMatchKind.REQUIRES_CONVERSION) { match = ArgumentsMatchKind.CLOSE; } } // 看被解析的方法参数类型能不能转换成方法参数类型 // 比如我们表达式中配置的是Integer类型,String类中的方法concat方法是String类型, // Integer类型是能转换成String类型的,因此,canConvert 返回 true else if (typeConverter.canConvert(suppliedArg, expectedArg)) { match = ArgumentsMatchKind.REQUIRES_CONVERSION; } else { match = null; } } } } // 返回参数匹配 ArgumentsMatchInfo 对象 return (match != null ? new ArgumentsMatchInfo(match) : null); }
@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); 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); } }
static boolean convertArguments(TypeConverter converter, Object[] arguments, Object methodOrCtor, Integer varargsPosition) throws EvaluationException { boolean conversionOccurred = false; // 如果不是可变参数,varargsPosition为null if (varargsPosition == null) { for (int i = 0; i < arguments.length; i++) { TypeDescriptor targetType = new TypeDescriptor(MethodParameter.forMethodOrConstructor(methodOrCtor, i)); Object argument = arguments[i]; // 在这里是将我们配置的方法参数转换成需要被调用的方法参数类型, // 如在本例中,我们将 '!' 字符串类型转换成String类concat方法中的参数类型String // 如果我们配置的是1,将会将Integer类型类型转换成String类concat方法中的参数类型String arguments[i] = converter.convertValue(argument, TypeDescriptor.forObject(argument), targetType); conversionOccurred |= (argument != arguments[i]); } } else { // 下面是对于可变参数的处理 // Convert everything up to the varargs position for (int i = 0; i < varargsPosition; i++) { TypeDescriptor targetType = new TypeDescriptor(MethodParameter.forMethodOrConstructor(methodOrCtor, i)); Object argument = arguments[i]; arguments[i] = converter.convertValue(argument, TypeDescriptor.forObject(argument), targetType); conversionOccurred |= (argument != arguments[i]); } MethodParameter methodParam = MethodParameter.forMethodOrConstructor(methodOrCtor, varargsPosition); if (varargsPosition == arguments.length - 1) { // If the target is varargs and there is just one more argument // then convert it here TypeDescriptor targetType = new TypeDescriptor(methodParam); Object argument = arguments[varargsPosition]; TypeDescriptor sourceType = TypeDescriptor.forObject(argument); arguments[varargsPosition] = converter.convertValue(argument, sourceType, targetType); // Three outcomes of that previous line: // 1) the input argument was already compatible (ie. array of valid type) and nothing was done // 2) the input argument was correct type but not in an array so it was made into an array // 3) the input argument was the wrong type and got converted and put into an array if (argument != arguments[varargsPosition] && !isFirstEntryInArray(argument, arguments[varargsPosition])) { conversionOccurred = true; // case 3 } } else { // Convert remaining arguments to the varargs element type TypeDescriptor targetType = new TypeDescriptor(methodParam).getElementTypeDescriptor(); for (int i = varargsPosition; i < arguments.length; i++) { Object argument = arguments[i]; arguments[i] = converter.convertValue(argument, TypeDescriptor.forObject(argument), targetType); conversionOccurred |= (argument != arguments[i]); } } } return conversionOccurred; }
到这里终于看到了反射调用方法了。最终调用了’Hello World’的concat方法并传入字符串’!’
得到Hello World!
终于解析出我们想要的值。
最终将得到的字符串Hello World! 强转成String类型。
到这里,我们终于完成了字符串的解析及值的获取 。最终,我们来总结一下。
1.分词:经过一系列的字符串操作,得到字符数组,‘Hello Word’ ,. , concat , ‘!’, ( ,)
2.吃掉表达式:将表达式封装成CompoundExpression对象,对象中有两个child,第一个是StringLiteral(‘Hello World’),第二个是MethodReference(‘concat’),而MethodReference中有一个StringLiteral(’!’)的child 。
3.值获取:根据’Hello World’获取类型String,并获取String类型所有的方法,根据方法名称和参数类型匹配出最合适的方法。
4.通过反射调用匹配出的方法。
5.如果用户传递类型,则将反射得到的值转换成用户需要的类型。