之前的博客中解析了(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.如果用户传递类型,则将反射得到的值转换成用户需要的类型。