Spring源码深度解析(郝佳)-学习-Spel源码解析(一)-运算符计算

20 篇文章 1 订阅
18 篇文章 0 订阅

在网上,我看到关于Spring的Spel表达式源码解析相关文章相对较少,因此,通过几天的研究,来写一下关系Spring的Spel表达式源码解析相关的文章,希望对大家有帮助
二话不说,直接上代码

@Test
public void test11_math_1() {
    ExpressionParser parser = new SpelExpressionParser();
    int two = parser.parseExpression("(21 + 3) * 4").getValue(Integer.class); // 2
    System.out.println(two);
}

【结果输出】
96

TemplateAwareExpressionParser.java

@Override
public Expression parseExpression(String expressionString) throws ParseException {
	return parseExpression(expressionString, NON_TEMPLATE_PARSER_CONTEXT);
}

而NON_TEMPLATE_PARSER_CONTEXT值是一个ParserContext对象,实现为

TemplateAwareExpressionParser.java

private static final ParserContext NON_TEMPLATE_PARSER_CONTEXT = new ParserContext() {
	@Override
	//需要解析的表达式前缀 
	public String getExpressionPrefix() {
		return null;
	}
	//需要解析的表达式后缀 
	@Override
	public String getExpressionSuffix() {
		return null;
	}
	//是不是使用模版
	@Override
	public boolean isTemplate() {
		return false;
	}
};

TemplateAwareExpressionParser.java

@Override
public Expression parseExpression(String expressionString, ParserContext context)
		throws ParseException {
	if (context == null) {
		//设置默认的ParserContext
		context = NON_TEMPLATE_PARSER_CONTEXT;
	}
	//是不是使用模版解析方法,这个方法后面文章再来解析了
	if (context.isTemplate()) {
		return parseTemplate(expressionString, context);
	}
	else {
		return doParseExpression(expressionString, context);
	}
}

SpelExpressionParser.java

@Override
protected SpelExpression doParseExpression(String expressionString, ParserContext context) throws ParseException {
	//如果在new SpelExpressionParser这个对象时没有传入,则使用默认的SpelParserConfiguration实例作为configuration传入参数 
	return new InternalSpelExpressionParser(this.configuration).doParseExpression(expressionString, context);
}

经过上面的层层递进,终于到了核心方法了。

InternalSpelExpressionParser.java

@Override
protected SpelExpression doParseExpression(String expressionString, ParserContext context) throws ParseException {
	try {
		this.expressionString = expressionString;
		//表达式分词
		Tokenizer tokenizer = new Tokenizer(expressionString);
		tokenizer.process();
		this.tokenStream = tokenizer.getTokens();
		this.tokenStreamLength = this.tokenStream.size();
		this.tokenStreamPointer = 0;
		this.constructedNodes.clear();
		//将表达式解析成SpelNodeImpl对象,这个方法很重要
		SpelNodeImpl ast = eatExpression();
		if (moreTokens()) {
			throw new SpelParseException(peekToken().startPos, SpelMessage.MORE_INPUT, toString(nextToken()));
		}
		//解析出来的表达式统一封装返回
		return new SpelExpression(expressionString, ast, this.configuration);
	}
	catch (InternalParseException ex) {
		throw ex.getCause();
	}
}

首先我们来看一下分词是如何实现的

Tokenizer.java

public Tokenizer(String inputData) {
	//保留原始字符串
	this.expressionString = inputData;
	//将字符串分隔成字符数组
	this.toProcess = (inputData + "\0").toCharArray();
	//设置字符串最大长度
	this.max = this.toProcess.length;
	//初始化当前分词位置为0
	this.pos = 0;
	process();
}

在这里插入图片描述

Tokenizer.java

public void process() {
	//循环遍历字符数组
	while (this.pos < this.max) {
		char ch = this.toProcess[this.pos];
		//如果ch是字母
		if (isAlphabetic(ch)) {
			lexIdentifier();
		}
		else {
			switch (ch) {
				case '+':
					//如果字符是++
					if (isTwoCharToken(TokenKind.INC)) {
						pushPairToken(TokenKind.INC);
					}
					else {
						//如果字符是+
						pushCharToken(TokenKind.PLUS);
					}
					break;
				case '_': //如果字符是下划线
					lexIdentifier();
					break;
				case '-':
					//如果字符是--
					if (isTwoCharToken(TokenKind.DEC)) {
						pushPairToken(TokenKind.DEC);
					}
					else {
						//字符是 - 
						pushCharToken(TokenKind.MINUS);
					}
					break;
				case ':':
					//如果字符是分号 
					pushCharToken(TokenKind.COLON);
					break;
				case '.'://如果字符是点 
					pushCharToken(TokenKind.DOT);
					break;
				case ','://如果字符是逗号 
					pushCharToken(TokenKind.COMMA);
					break;
				case '*'://如果字符是乘 
					pushCharToken(TokenKind.STAR);
					break;
				case '/'://如果字符是除 
					pushCharToken(TokenKind.DIV);
					break;
				case '%'://如果字符是取余 
					pushCharToken(TokenKind.MOD);
					break;
				case '('://如果字符是左小括号 
					pushCharToken(TokenKind.LPAREN);
					break;
				case ')'://如果字符是右小括号 
					pushCharToken(TokenKind.RPAREN);
					break;
				case '['://如果字符是左中括号 
					pushCharToken(TokenKind.LSQUARE);
					break;
				case '#'://如果字符是井号 
					pushCharToken(TokenKind.HASH);
					break;
				case ']'://如果字符是右中括号 
					pushCharToken(TokenKind.RSQUARE);
					break;
				case '{'://如果字符是左大括号 
					pushCharToken(TokenKind.LCURLY);
					break;
				case '}'://如果字符是右大括号 
					pushCharToken(TokenKind.RCURLY);
					break;
				case '@'://如果字符是@符号
					pushCharToken(TokenKind.BEAN_REF);
					break;
				case '^':
					//如果字符是 ^[ 
					if (isTwoCharToken(TokenKind.SELECT_FIRST)) {
						pushPairToken(TokenKind.SELECT_FIRST);
					}
					else {
						//如果字符是 ^ 
						pushCharToken(TokenKind.POWER);
					}
					break;
				case '!':
					//如果字符是 != 
					if (isTwoCharToken(TokenKind.NE)) {
						pushPairToken(TokenKind.NE);
					}
					//如果字符是 ![ 
					else if (isTwoCharToken(TokenKind.PROJECT)) {
						pushPairToken(TokenKind.PROJECT);
					}
					else {
						//如果字符是 ! 
						pushCharToken(TokenKind.NOT);
					}
					break;
				case '=':
					//如果字符是 == 
					if (isTwoCharToken(TokenKind.EQ)) {
						pushPairToken(TokenKind.EQ);
					}
					else {
						//如果字符是 = 
						pushCharToken(TokenKind.ASSIGN);
					}
					break;
				case '&':
					//如果字符是& ,但是不是 && 抛出异常
					if (!isTwoCharToken(TokenKind.SYMBOLIC_AND)) {
						throw new InternalParseException(new SpelParseException(
								this.expressionString, this.pos, SpelMessage.MISSING_CHARACTER,
								"&"));
					}
					pushPairToken(TokenKind.SYMBOLIC_AND);
					break;
				case '|':
					//如果字符是 |  ,但是不是 || 抛出异常
					if (!isTwoCharToken(TokenKind.SYMBOLIC_OR)) {
						throw new InternalParseException(new SpelParseException(
								this.expressionString, this.pos, SpelMessage.MISSING_CHARACTER,
								"|"));
					}
					pushPairToken(TokenKind.SYMBOLIC_OR);
					break;
				case '?':
					//如果字符是 ?[
					if (isTwoCharToken(TokenKind.SELECT)) {
						pushPairToken(TokenKind.SELECT);
					}
					//如果字符是 ?:
					else if (isTwoCharToken(TokenKind.ELVIS)) {
						pushPairToken(TokenKind.ELVIS);
					}
					//如果字符是 ?.
					else if (isTwoCharToken(TokenKind.SAFE_NAVI)) {
						pushPairToken(TokenKind.SAFE_NAVI);
					}
					//如果字符是 ?
					else {
						pushCharToken(TokenKind.QMARK);
					}
					break;
				case '$':
					//如果字符是 $[
					if (isTwoCharToken(TokenKind.SELECT_LAST)) {
						pushPairToken(TokenKind.SELECT_LAST);
					}
					//如果字符是 $
					else {
						lexIdentifier();
					}
					break;
				case '>':
					//如果字符是 >= 
					if (isTwoCharToken(TokenKind.GE)) {
						pushPairToken(TokenKind.GE);
					}
					else {
						//如果字符是 > 
						pushCharToken(TokenKind.GT);
					}
					break;
				case '<':
					//如果字符是 <= 
					if (isTwoCharToken(TokenKind.LE)) {
						pushPairToken(TokenKind.LE);
					}
					else {
						//如果字符是 < 
						pushCharToken(TokenKind.LT);
					}
					break;
				case '0':
				case '1':
				case '2':
				case '3':
				case '4':
				case '5':
				case '6':
				case '7':
				case '8':
				case '9':
					// 数字处理
					lexNumericLiteral(ch == '0');
					break;
				case ' ':
				case '\t':
				case '\r':
				case '\n':
					// 空格,回车,tab 处理
					this.pos++;
					break;
				case '\'':
					//单引号处理 
					lexQuotedStringLiteral();
					break;
				case '"':
					//双引号处理 
					lexDoubleQuotedStringLiteral();
					break;
				case 0:
					//0 处理 
					this.pos++; // will take us to the end
					break;
				case '\\':// 单斜杠处理,抛出异常 
					throw new InternalParseException(
							new SpelParseException(this.expressionString, this.pos, SpelMessage.UNEXPECTED_ESCAPE_CHAR));
				default:
					throw new IllegalStateException("Cannot handle (" + Integer.valueOf(ch) + ") '" + ch + "'");
			}
		}
	}
}

首先是左括号的处理,传入枚举类型为 TokenKind 的LPAREN("(")

private void pushCharToken(TokenKind kind) {
	this.tokens.add(new Token(kind, this.pos, this.pos + 1));
	//解析字符串的下标向后移动一位
	this.pos++;
}

Token对象有四个参数

class Token {
	//枚举类型,比如说是++ ,-- 等  
	TokenKind kind;
	//具体的值,如果是字符串解析就有相应的值,如果是符号解析,这个值为空  
	String data;
	//本token的值,从被解析字符串截取的起始位置 
	int startPos;  // index of first character
	//本token的值,从被解析字符串截取的未位位置  
	int endPos;  // index of char after the last character


	// + - * / 等符号位直接调用
	Token(TokenKind tokenKind, int startPos, int endPos) {
		this.kind = tokenKind;
		this.startPos = startPos;
		this.endPos = endPos;
	}

	Token(TokenKind tokenKind, char[] tokenData, int startPos, int endPos) {
		this(tokenKind, startPos, endPos);
		this.data = new String(tokenData);
	}


	public TokenKind getKind() {
		return this.kind;
	}

	@Override
	public String toString() {
		StringBuilder s = new StringBuilder();
		s.append("[").append(this.kind.toString());
		if (this.kind.hasPayload()) {
			s.append(":").append(this.data);
		}
		s.append("]");
		s.append("(").append(this.startPos).append(",").append(this.endPos).append(")");
		return s.toString();
	}

	public boolean isIdentifier() {
		return (this.kind == TokenKind.IDENTIFIER);
	}

	public boolean isNumericRelationalOperator() {
		return (this.kind == TokenKind.GT || this.kind == TokenKind.GE || this.kind == TokenKind.LT ||
				this.kind == TokenKind.LE || this.kind==TokenKind.EQ || this.kind==TokenKind.NE);
	}

	public String stringValue() {
		return this.data;
	}

	public Token asInstanceOfToken() {
		return new Token(TokenKind.INSTANCEOF, this.startPos, this.endPos);
	}

	public Token asMatchesToken() {
		return new Token(TokenKind.MATCHES, this.startPos, this.endPos);
	}

	public Token asBetweenToken() {
		return new Token(TokenKind.BETWEEN, this.startPos, this.endPos);
	}

}

被解析的字符串下标右移,此时this.pos值等于1 ,在"(21 + 3) * 4" 字符串中对应的位置是 2 ,而数字对应的方法是lexNumericLiteral,解析如下

Tokenizer.java

private void lexNumericLiteral(boolean firstCharIsZero) {
	boolean isReal = false;
	int start = this.pos;
	char ch = this.toProcess[this.pos + 1];
	// 拿到字符串的下一位,是 x 或者X ,如果是,则是十六进制
	boolean isHex = ch == 'x' || ch == 'X';

	// 如果首位是0并且是十六进制数
	if (firstCharIsZero && isHex) {
		this.pos = this.pos + 1;
		do {
			this.pos++;
		}
		while (isHexadecimalDigit(this.toProcess[this.pos]));
		if (isChar('L', 'l')) {
			pushHexIntToken(subarray(start + 2, this.pos), true, start, this.pos);
			this.pos++;
		}
		else {
			pushHexIntToken(subarray(start + 2, this.pos), false, start, this.pos);
		}
		return;
	}
	//下标不断右移,直到没有数字为止 
	do {
		this.pos++;
	}
	while (isDigit(this.toProcess[this.pos]));

	//如果数字后的第一个字符串是 . 
	ch = this.toProcess[this.pos];
	if (ch == '.') {
		isReal = true;
		int dotpos = this.pos;
		// carry on consuming digits
		do {
			this.pos++;
		}
		while (isDigit(this.toProcess[this.pos]));
		if (this.pos == dotpos + 1) {
			// the number is something like '3.'. It is really an int but may be
			// part of something like '3.toString()'. In this case process it as
			// an int and leave the dot as a separate token.
			this.pos = dotpos;
			pushIntToken(subarray(start, this.pos), false, start, this.pos);
			return;
		}
	}
	//保留下标位置,本次解析字符串的截取 
	int endOfNumber = this.pos;

	//如果数字字符串之后的第一个字符是L 或者 l ,表示当前数字是Long类型
	if (isChar('L', 'l')) {
		if (isReal) { // 3.4L - not allowed
			throw new InternalParseException(new SpelParseException(this.expressionString,
					start, SpelMessage.REAL_CANNOT_BE_LONG));
		}
		pushIntToken(subarray(start, endOfNumber), true, start, endOfNumber);
		this.pos++;
	}
	//如果是E或者e ,表示科学记数法
	else if (isExponentChar(this.toProcess[this.pos])) {
		isReal = true; // if it wasn't before, it is now
		this.pos++;
		char possibleSign = this.toProcess[this.pos];
		if (isSign(possibleSign)) {
			this.pos++;
		}

		// exponent digits
		do {
			this.pos++;
		}
		while (isDigit(this.toProcess[this.pos]));
		boolean isFloat = false;
		if (isFloatSuffix(this.toProcess[this.pos])) {
			isFloat = true;
			endOfNumber = ++this.pos;
		}
		else if (isDoubleSuffix(this.toProcess[this.pos])) {
			endOfNumber = ++this.pos;
		}
		pushRealToken(subarray(start, this.pos), isFloat, start, this.pos);
	}
	else {
		//我们知道 21 后面的字符串是空格,此时ch是空格
		ch = this.toProcess[this.pos];
		boolean isFloat = false;
		//是不是符点数
		if (isFloatSuffix(ch)) {
			isReal = true;
			isFloat = true;
			endOfNumber = ++this.pos;
		}
		//是不是double类型
		else if (isDoubleSuffix(ch)) {
			isReal = true;
			endOfNumber = ++this.pos;
		}
		//如果数字是符点类型或者double类型
		if (isReal) {
			pushRealToken(subarray(start, endOfNumber), isFloat, start, endOfNumber);
		}
		else {
			//是整形或者Long类型
			pushIntToken(subarray(start, endOfNumber), false, start, endOfNumber);
		}
	}
}

在这里插入图片描述
从断点来看,最后进入了pushIntToken方法,那么我们来总结一下这个方法,方法进入先判断当前数字是不是十六进制数,如果不是,则解析当前数字后的所有的数字 ,如 (21 + 3) * 4 ,如果第一次读取到2,则读尽与2相邻的所有的数字,最后读取到1后面的空格,再通过空格与F 或者 L 或者E 或者D 之类的相比对,看是不是符点类型或者Long类型或者科学计数法或者Double类型,如果都不是,则直接创建Token对象,并保存到 tokens 数组中。如下图所示

在这里插入图片描述
我们完成了21的解析,21后面的字符串是空格,因此,代码继续让解析下标右移。
在这里插入图片描述
空格后面是+号,因此,继续调用pushCharToken方法,保存Token对象
在这里插入图片描述
加号后面是空格,解析下标继续右移

在这里插入图片描述
加号后面是 3 ,代码如图所示。
以同样的方法,我们解析出字符串中所有的字符,并将他们封装成一个Token集合,保存到Tokenizer的tokens属性中。
在这里插入图片描述
在这里插入图片描述

通过分词器,我们得到了一个Token集合。我们再来看看eatExpression方法,从语义上看这个方法是【吃掉表达式】的意思,那是如何吃掉表达式的呢?在进入方法之前,我们来了解一下吃掉表达式中几个常用方法。

InternalSpelExpressionParser.java

moreTokens

private boolean moreTokens() {
	return this.tokenStreamPointer<this.tokenStream.size();
}

this.tokenStreamPointer表示当前Token集合下标
this.tokenStream.size() 表示当前Token集合长度。
因为在吃掉表达式的方法中,每解析掉一个或者多个Token时候,集合下标增加。当moreTokens返回false时,解析结束。

peekToken

private Token peekToken() {
	if (this.tokenStreamPointer >= this.tokenStreamLength) {
		return null;
	}
	return this.tokenStream.get(this.tokenStreamPointer);
}

不难看出,返回当前下标对应的Token

带参数peekToken

private boolean peekToken(TokenKind possible1,TokenKind possible2) {
	if (!moreTokens()) {
		return false;
	}
	Token t = peekToken();
	return (t.kind == possible1 || t.kind == possible2);
}

当前Token的类型是不是传入的类型,如果当前Token是+ ,传入的类型也是+ ,返回true 。否则,false。

nextToken

private Token nextToken() {
	if (this.tokenStreamPointer >= this.tokenStreamLength) {
		return null;
	}
	return this.tokenStream.get(this.tokenStreamPointer++);
}

从这个方法中不难看出,nextToken 主要目标是返回当前下标的Token,并且让下标+1 ,那么下次再调用peekToken()方法或者nextToken()方法时,将返回当前下标对应的Token的下一个Token。

push

private final Stack constructedNodes = new Stack();

private void push(SpelNodeImpl newNode) {
	this.constructedNodes.push(newNode);
}

pop

private SpelNodeImpl pop() {
	return this.constructedNodes.pop();
}

constructedNodes这是一个栈结构,push方法每次向里面存数据。pop方法每次从栈中弹出数据。

我们了解了上面几个方法后,我们来分析代码

InternalSpelExpressionParser.java

private SpelNodeImpl eatExpression() {
	SpelNodeImpl expr = eatLogicalOrExpression();
	if (moreTokens()) {
		Token t = peekToken();
		if (t.kind == TokenKind.ASSIGN) {  // a=b
			if (expr == null) {
				expr = new NullLiteral(toPos(t.startPos - 1, t.endPos - 1));
			}
			nextToken();
			SpelNodeImpl assignedValue = eatLogicalOrExpression();
			return new Assign(toPos(t), expr, assignedValue);
		}

		if (t.kind == TokenKind.ELVIS) {  // a?:b (a if it isn't null, otherwise b)
			if (expr == null) {
				expr = new NullLiteral(toPos(t.startPos - 1, t.endPos - 2));
			}
			nextToken();  // elvis has left the building
			SpelNodeImpl valueIfNull = eatExpression();
			if (valueIfNull==null) {
				valueIfNull = new NullLiteral(toPos(t.startPos + 1, t.endPos + 1));
			}
			return new Elvis(toPos(t), expr, valueIfNull);
		}

		if (t.kind == TokenKind.QMARK) {  // a?b:c
			if (expr == null) {
				expr = new NullLiteral(toPos(t.startPos - 1, t.endPos - 1));
			}
			nextToken();
			SpelNodeImpl ifTrueExprValue = eatExpression();
			eatToken(TokenKind.COLON);
			SpelNodeImpl ifFalseExprValue = eatExpression();
			return new Ternary(toPos(t), expr, ifTrueExprValue, ifFalseExprValue);
		}
	}
	return expr;
}
//吃掉 OR 或者 || 
private SpelNodeImpl eatLogicalOrExpression() {
	SpelNodeImpl expr = eatLogicalAndExpression();
	while (peekIdentifierToken("or") || peekToken(TokenKind.SYMBOLIC_OR)) {
		Token t = nextToken();  //consume OR
		SpelNodeImpl rhExpr =eatLogicalAndExpression();
		checkOperands(t, expr, rhExpr);
		expr = new OpOr(toPos(t), expr, rhExpr);
	}
	return expr;
}
//吃掉 and 或者 && 
private SpelNodeImpl eatLogicalAndExpression() {
	SpelNodeImpl expr = eatRelationalExpression();
	while (peekIdentifierToken("and") || peekToken(TokenKind.SYMBOLIC_AND)) {
		Token t = nextToken();  // consume 'AND'
		SpelNodeImpl rhExpr = eatRelationalExpression();
		checkOperands(t, expr, rhExpr);
		expr = new OpAnd(toPos(t), expr, rhExpr);
	}
	return expr;
}

//吃掉 > ,< ,>= ,<=, == 等关系表达式 
private SpelNodeImpl eatRelationalExpression() {
	SpelNodeImpl expr = eatSumExpression();
	Token relationalOperatorToken = maybeEatRelationalOperator();
	if (relationalOperatorToken != null) {
		Token t = nextToken();  // consume relational operator token
		SpelNodeImpl rhExpr = eatSumExpression();
		checkOperands(t, expr, rhExpr);
		TokenKind tk = relationalOperatorToken.kind;

		if (relationalOperatorToken.isNumericRelationalOperator()) {
			int pos = toPos(t);
			if (tk == TokenKind.GT) {
				return new OpGT(pos, expr, rhExpr);
			}
			if (tk == TokenKind.LT) {
				return new OpLT(pos, expr, rhExpr);
			}
			if (tk == TokenKind.LE) {
				return new OpLE(pos, expr, rhExpr);
			}
			if (tk == TokenKind.GE) {
				return new OpGE(pos, expr, rhExpr);
			}
			if (tk == TokenKind.EQ) {
				return new OpEQ(pos, expr, rhExpr);
			}
			Assert.isTrue(tk == TokenKind.NE);
			return new OpNE(pos, expr, rhExpr);
		}

		if (tk == TokenKind.INSTANCEOF) {
			return new OperatorInstanceof(toPos(t), expr, rhExpr);
		}

		if (tk == TokenKind.MATCHES) {
			return new OperatorMatches(toPos(t), expr, rhExpr);
		}

		Assert.isTrue(tk == TokenKind.BETWEEN);
		return new OperatorBetween(toPos(t), expr, rhExpr);
	}
	return expr;
}
//吃掉 * , / , % 表达式  
private SpelNodeImpl eatSumExpression() {
	SpelNodeImpl expr = eatProductExpression();
	while (peekToken(TokenKind.PLUS, TokenKind.MINUS, TokenKind.INC)) {
		Token t = nextToken();//consume PLUS or MINUS or INC
		SpelNodeImpl rhExpr = eatProductExpression();
		checkRightOperand(t,rhExpr);
		if (t.kind == TokenKind.PLUS) {
			expr = new OpPlus(toPos(t), expr, rhExpr);
		}
		else if (t.kind == TokenKind.MINUS) {
			expr = new OpMinus(toPos(t), expr, rhExpr);
		}
	}
	return expr;
}
//吃掉  ^  
private SpelNodeImpl eatPowerIncDecExpression() {
	SpelNodeImpl expr = eatUnaryExpression();
	if (peekToken(TokenKind.POWER)) {
		Token t = nextToken();  //consume POWER
		SpelNodeImpl rhExpr = eatUnaryExpression();
		checkRightOperand(t,rhExpr);
		return new OperatorPower(toPos(t), expr, rhExpr);
	}

	if (expr != null && peekToken(TokenKind.INC,TokenKind.DEC)) {
		Token t = nextToken();  //consume INC/DEC
		if (t.getKind() == TokenKind.INC) {
			return new OpInc(toPos(t), true, expr);
		}
		return new OpDec(toPos(t), true, expr);
	}

	return expr;
}


//吃掉  + ,- ,! ,++,-- 表达式 
private SpelNodeImpl eatUnaryExpression() {
	if (peekToken(TokenKind.PLUS, TokenKind.MINUS, TokenKind.NOT)) {
		Token t = nextToken();
		SpelNodeImpl expr = eatUnaryExpression();
		if (t.kind == TokenKind.NOT) {
			return new OperatorNot(toPos(t), expr);
		}

		if (t.kind == TokenKind.PLUS) {
			return new OpPlus(toPos(t), expr);
		}
		Assert.isTrue(t.kind == TokenKind.MINUS);
		return new OpMinus(toPos(t), expr);

	}
	if (peekToken(TokenKind.INC, TokenKind.DEC)) {
		Token t = nextToken();
		SpelNodeImpl expr = eatUnaryExpression();
		if (t.getKind() == TokenKind.INC) {
			return new OpInc(toPos(t), false, expr);
		}
		return new OpDec(toPos(t), false, expr);
	}
	//如果以上都不是,那么只能吃掉主表达式了
	return eatPrimaryExpression();
}

private SpelNodeImpl eatPrimaryExpression() {
	List nodes = new ArrayList();
	SpelNodeImpl start = eatStartNode();  // always a start node
	nodes.add(start);
	//也许吃掉的Token,但不一定能吃得下
	while (maybeEatNode()) {
		nodes.add(pop());
	}
	if (nodes.size() == 1) {
		return nodes.get(0);
	}
	//复杂表达式
	return new CompoundExpression(toPos(start.getStartPosition(),
			nodes.get(nodes.size() - 1).getEndPosition()),
			nodes.toArray(new SpelNodeImpl[nodes.size()]));
}
private SpelNodeImpl eatStartNode() {
	if (maybeEatLiteral()) {	// tokens[0] 是 ( ,返回false 
		return pop();
	}
	else if (maybeEatParenExpression()) {// tokens[0] 是 ( ,则进行处理 
		return pop();
	}
	else if (maybeEatTypeReference() || maybeEatNullReference() || maybeEatConstructorReference() ||
			maybeEatMethodOrProperty(false) || maybeEatFunctionOrVar()) {
		return pop();
	}
	else if (maybeEatBeanReference()) {
		return pop();
	}
	else if (maybeEatProjection(false) || maybeEatSelection(false) || maybeEatIndexer()) {
		return pop();
	}
	else if (maybeEatInlineListOrMap()) {
		return pop();
	}
	else {
		return null;
	}
}

private boolean maybeEatLiteral() {
	Token t = peekToken();
	//如果token为空,直接返回false
	if (t == null) {
		return false;
	}
	//如果是int
	if (t.kind == TokenKind.LITERAL_INT) {
		push(Literal.getIntLiteral(t.data, toPos(t), 10));
	}
	//如果是long
	else if (t.kind == TokenKind.LITERAL_LONG) {
		push(Literal.getLongLiteral(t.data, toPos(t), 10));
	}
	else if (t.kind == TokenKind.LITERAL_HEXINT) {
		push(Literal.getIntLiteral(t.data, toPos(t), 16));
	}
	//如果是十六进制长整形
	else if (t.kind == TokenKind.LITERAL_HEXLONG) {
		push(Literal.getLongLiteral(t.data, toPos(t), 16));
	}
	else if (t.kind == TokenKind.LITERAL_REAL) {
		push(Literal.getRealLiteral(t.data, toPos(t), false));
	}
	else if (t.kind == TokenKind.LITERAL_REAL_FLOAT) {
		push(Literal.getRealLiteral(t.data, toPos(t), true));
	}
	else if (peekIdentifierToken("true")) {
		push(new BooleanLiteral(t.data, toPos(t), true));
	}
	else if (peekIdentifierToken("false")) {
		push(new BooleanLiteral(t.data, toPos(t), false));
	}
	else if (t.kind == TokenKind.LITERAL_STRING) {
		push(new StringLiteral(t.data, toPos(t), t.data));
	}
	else {
		return false;
	}
	nextToken();
	return true;
}
// token 是 ( ,则进行处理 
private boolean maybeEatParenExpression() {
	// token是左括号
	if (peekToken(TokenKind.LPAREN)) {
		//下标右移 
		nextToken();
		// 递归调用,吃掉括号里的表达式 【 21 + 3 】 
		SpelNodeImpl expr = eatExpression();
		//吃掉右括号
		eatToken(TokenKind.RPAREN);
		push(expr);
		return true;
	}
	else {
		return false;
	}
}

递归调用eatExpression 方法,21 将被maybeEatLiteral方法解析到。如图所示
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
将解析到的【21】的 IntLiteral对象返回。

在这里插入图片描述

同样的解析方式调用eatProductExpression方法,返回 【3】这个值的 的IntLiteral对象,
最后再构建OpPlus对象
在这里插入图片描述
这个对象里的方法,我们进去看看。
在这里插入图片描述
最后到了SpelNodeImpl方法。

SpelNodeImpl.java

public SpelNodeImpl(int pos, SpelNodeImpl... operands) {
	this.pos = pos;
	// pos combines start and end so can never be zero because tokens cannot be zero length
	Assert.isTrue(pos != 0, "Pos must not be 0");
	if (!ObjectUtils.isEmpty(operands)) {
		this.children = operands;
		for (SpelNodeImpl childNode : operands) {
			childNode.parent = this;
		}
	}
}

这个方法是什么意思呢?
我们先来看看数据 。
在这里插入图片描述
这个代码的意思就是
在这里插入图片描述
这个代码的意思其实在我们人类的世界比较简单,比如 父亲张三 有两个儿子小明 ,小林,那么,张三的儿子 就是 小明 和小林,小林的父亲 就是 张三 ,通过张三一定能找到他的两个儿子,通过任意一个儿子都能找到他们的父亲张三。这代码大概是这个意思。
当获取到IntLiteral对象后,下标右移,当前Token是 右括号
在这里插入图片描述
因此吃掉括号表达式里的值返回一个OpPlus对象,这个对象有两个child ,一个是 【21】的IntLiteral ,另一个是 【3】的IntLiteral 。
在这里插入图片描述
再次通过同样的方法来解析,得到 * 右边的表达式 【4】的IntLiteral 对象。而刚刚解析括号表达式内部的 21 + 3 得到OpPlus对象,通过左边【21 + 3 】OpPlus 和右边【4 】IntLiteral构建OpMultiply【(21 + 3 ) * 4 】并返回,到这里己经完成表达式的全部解析。
最后返回一个OpMultiply对象。
在这里插入图片描述
我们做一张图来表示一下。

在这里插入图片描述

上面己经完成了分词和表达式的解析。我们来分析一下,对象值的获取。

SpelExpression.java

@Override
//用户希望返回的类型 
public  T getValue(Class expectedResultType) throws EvaluationException {
	if (this.compiledAst != null) {
		try {
			TypedValue contextRoot = evaluationContext == null ? null : evaluationContext.getRootObject();
			Object result = this.compiledAst.getValue(contextRoot == null ? null : contextRoot.getValue(), evaluationContext);
			if (expectedResultType == null) {
				return (T)result;
			}
			else {
				return ExpressionUtils.convertTypedValue(getEvaluationContext(), new TypedValue(result), expectedResultType);
			}
		}
		catch (Throwable ex) {
			// If running in mixed mode, revert to interpreted
			if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) {
				this.interpretedCount = 0;
				this.compiledAst = null;
			}
			else {
				// Running in SpelCompilerMode.immediate mode - propagate exception to caller
				throw new SpelEvaluationException(ex, SpelMessage.EXCEPTION_RUNNING_COMPILED_EXPRESSION);
			}
		}
	}
	//初始化配置
	ExpressionState expressionState = new ExpressionState(getEvaluationContext(), this.configuration);
	// 获取表达式的类型和值
	TypedValue typedResultValue = this.ast.getTypedValue(expressionState);
	checkCompile(expressionState);
	// 将值转化成用户期望的类型并返回
	return ExpressionUtils.convertTypedValue>(expressionState.getEvaluationContext(), typedResultValue, expectedResultType);
}

SpelNodeImpl.java

@Override
public final TypedValue getTypedValue(ExpressionState expressionState) throws EvaluationException {
	if (expressionState != null) {
		return getValueInternal(expressionState);
	}
	else {
		return getTypedValue(new ExpressionState(new StandardEvaluationContext()));
	}
}

OpMultiply.java

@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
	//this.children[0] ,在本例中,返回OpPlus对象 ,调用OpPlus对象的获取值方法,返回24 
	Object leftOperand = getLeftOperand().getValueInternal(state).getValue();
	//this.children[1],在本例中,返回IntLiteral对象,再调用IntLiteral对象的getValue 方法,返回4 
	Object rightOperand = getRightOperand().getValueInternal(state).getValue();

	if (leftOperand instanceof Number && rightOperand instanceof Number) {
		Number leftNumber = (Number) leftOperand;
		Number rightNumber = (Number) rightOperand;

		if (leftNumber instanceof BigDecimal || rightNumber instanceof BigDecimal) {
			BigDecimal leftBigDecimal = NumberUtils.convertNumberToTargetClass(leftNumber, BigDecimal.class);
			BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class);
			return new TypedValue(leftBigDecimal.multiply(rightBigDecimal));
		}
		else if (leftNumber instanceof Double || rightNumber instanceof Double) {
			this.exitTypeDescriptor = "D";
			return new TypedValue(leftNumber.doubleValue() * rightNumber.doubleValue());
		}
		else if (leftNumber instanceof Float || rightNumber instanceof Float) {
			this.exitTypeDescriptor = "F";
			return new TypedValue(leftNumber.floatValue() * rightNumber.floatValue());
		}
		else if (leftNumber instanceof BigInteger || rightNumber instanceof BigInteger) {
			BigInteger leftBigInteger = NumberUtils.convertNumberToTargetClass(leftNumber, BigInteger.class);
			BigInteger rightBigInteger = NumberUtils.convertNumberToTargetClass(rightNumber, BigInteger.class);
			return new TypedValue(leftBigInteger.multiply(rightBigInteger));
		}
		else if (leftNumber instanceof Long || rightNumber instanceof Long) {
			this.exitTypeDescriptor = "J";
			return new TypedValue(leftNumber.longValue() * rightNumber.longValue());
		}
		else if (CodeFlow.isIntegerForNumericOp(leftNumber) || CodeFlow.isIntegerForNumericOp(rightNumber)) {
			this.exitTypeDescriptor = "I";
			return new TypedValue(leftNumber.intValue() * rightNumber.intValue());
		}
		else {
			// Unknown Number subtypes -> best guess is double multiplication
			return new TypedValue(leftNumber.doubleValue() * rightNumber.doubleValue());
		}
	}

	if (leftOperand instanceof String && rightOperand instanceof Integer) {
		int repeats = (Integer) rightOperand;
		StringBuilder result = new StringBuilder();
		for (int i = 0; i < repeats; i++) {
			result.append(leftOperand);
		}
		return new TypedValue(result.toString());
	}

	return state.operate(Operation.MULTIPLY, leftOperand, rightOperand);
}

通过下图我们知道最终返回TypedValue对象,value=24*4 ,OpMultiply的exitTypeDescriptor的值为I 。到这里,我们终于得到了我们想要的值,下面,我们来看看类转换。
在这里插入图片描述

我们来看看OpPlus类的getValueInternal方法

OpPlus.java

@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
	//拿到左孩子,本例中返回【21】的IntLiteral
	SpelNodeImpl leftOp = getLeftOperand();
	//拿到右孩子, 本例中返回【3】的IntLiteral
	SpelNodeImpl rightOp = getRightOperand();

	if (rightOp == null) {  // if only one operand, then this is unary plus
		Object operandOne = leftOp.getValueInternal(state).getValue();
		if (operandOne instanceof Number) {
			if (operandOne instanceof Double) {
				this.exitTypeDescriptor = "D";
			}
			else if (operandOne instanceof Float) {
				this.exitTypeDescriptor = "F";
			}
			else if (operandOne instanceof Long) {
				this.exitTypeDescriptor = "J";
			}
			else if (operandOne instanceof Integer) {
				this.exitTypeDescriptor = "I";
			}
			return new TypedValue(operandOne);
		}
		return state.operate(Operation.ADD, operandOne, null);
	}
	
	TypedValue operandOneValue = leftOp.getValueInternal(state);
	// 本例中 获取到 左孩子的值 21 
	Object leftOperand = operandOneValue.getValue();
	TypedValue operandTwoValue = rightOp.getValueInternal(state);
	//本例中 获取到 右孩子的值 3  
	Object rightOperand = operandTwoValue.getValue();
	//左右孩子的值都是Number类型 
	if (leftOperand instanceof Number && rightOperand instanceof Number) {
		Number leftNumber = (Number) leftOperand;
		Number rightNumber = (Number) rightOperand;

		if (leftNumber instanceof BigDecimal || rightNumber instanceof BigDecimal) {
			BigDecimal leftBigDecimal = NumberUtils.convertNumberToTargetClass(leftNumber, BigDecimal.class);
			BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class);
			return new TypedValue(leftBigDecimal.add(rightBigDecimal));
		}
		else if (leftNumber instanceof Double || rightNumber instanceof Double) {
			this.exitTypeDescriptor = "D";
			return new TypedValue(leftNumber.doubleValue() + rightNumber.doubleValue());
		}
		else if (leftNumber instanceof Float || rightNumber instanceof Float) {
			this.exitTypeDescriptor = "F";
			return new TypedValue(leftNumber.floatValue() + rightNumber.floatValue());
		}
		else if (leftNumber instanceof BigInteger || rightNumber instanceof BigInteger) {
			BigInteger leftBigInteger = NumberUtils.convertNumberToTargetClass(leftNumber, BigInteger.class);
			BigInteger rightBigInteger = NumberUtils.convertNumberToTargetClass(rightNumber, BigInteger.class);
			return new TypedValue(leftBigInteger.add(rightBigInteger));
		}
		else if (leftNumber instanceof Long || rightNumber instanceof Long) {
			this.exitTypeDescriptor = "J";
			return new TypedValue(leftNumber.longValue() + rightNumber.longValue());
		}
		//如果是Integer,Short ,Byte类型
		else if (CodeFlow.isIntegerForNumericOp(leftNumber) || CodeFlow.isIntegerForNumericOp(rightNumber)) {
			// 设置OpPlus对象的exitTypeDescriptor为I 
			this.exitTypeDescriptor = "I";
			//返回TypedValue对象,value=21+3 ,typeDescriptor=null 
			return new TypedValue(leftNumber.intValue() + rightNumber.intValue());
		}
		else {
			// Unknown Number subtypes -> best guess is double addition
			return new TypedValue(leftNumber.doubleValue() + rightNumber.doubleValue());
		}
	}

	if (leftOperand instanceof String && rightOperand instanceof String) {
		this.exitTypeDescriptor = "Ljava/lang/String";
		return new TypedValue((String) leftOperand + rightOperand);
	}

	if (leftOperand instanceof String) {
		return new TypedValue(
				leftOperand + (rightOperand == null ? "null" : convertTypedValueToString(operandTwoValue, state)));
	}

	if (rightOperand instanceof String) {
		return new TypedValue(
				(leftOperand == null ? "null" : convertTypedValueToString(operandOneValue, state)) + rightOperand);
	}

	return state.operate(Operation.ADD, leftOperand, rightOperand);
}

在这里插入图片描述
我们再次回到刚刚的代码
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

细细一看,原来在getTypeDescriptor方法,做了一些改动
在这里插入图片描述
直接根据24*4的返回值 获取到了typeDescriptor

下面,我们来继续跟踪代码
在这里插入图片描述
需要我们将96这个值转换成Interger ,调用了ExpressionUtils的convertTypedValue方法。

ExpressionUtils.java

public static  T convertTypedValue(EvaluationContext context, TypedValue typedValue, Class targetType) {
	Object value = typedValue.getValue();
	//如果用户没有指定,直接强转为泛型类型
	if (targetType == null) {
		return (T) value;
	}
	if (context != null) {
		//获取类型转换器,进行转换,默认是StandardTypeConverter类
		return (T) context.getTypeConverter().convertValue(
				value, typedValue.getTypeDescriptor(), TypeDescriptor.valueOf(targetType));
	}
	if (ClassUtils.isAssignableValue(targetType, value)) {
		return (T) value;
	}
	throw new EvaluationException("Cannot convert value '" + value + "' to type '" + targetType.getName() + "'");
}

StandardTypeConverter.java

@Override
public Object convertValue(Object value, TypeDescriptor sourceType, TypeDescriptor targetType) {
	try {
		return this.conversionService.convert(value, sourceType, targetType);
	}
	catch (ConversionException ex) {
		throw new SpelEvaluationException(
				ex, SpelMessage.TYPE_CONVERSION_ERROR, sourceType.toString(), targetType.toString());
	}
}

在这里插入图片描述
在代码中,我们可以看到源类型是Integer类型,目标类型也是Integer类型。

GenericConversionService.java

@Override
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
	Assert.notNull(targetType, "targetType to convert to cannot be null");
	if (sourceType == null) {
		Assert.isTrue(source == null, "source must be [null] if sourceType == [null]");
		return handleResult(null, targetType, convertNullSource(null, targetType));
	}
	if (source != null && !sourceType.getObjectType().isInstance(source)) {
		throw new IllegalArgumentException("source to convert from must be an instance of " +
				sourceType + "; instead it was a " + source.getClass().getName());
	}
	GenericConverter converter = getConverter(sourceType, targetType);
	if (converter != null) {
		Object result = ConversionUtils.invokeConverter(converter, source, sourceType, targetType);
		return handleResult(sourceType, targetType, result);
	}
	return handleConverterNotFound(source, sourceType, targetType);
}

关系有点诡异,不是说调用默认的DefaultConversionService类嘛,怎么变成了GenericConversionService类了,我们看一下类关系图
在这里插入图片描述
原来如此,DefaultConversionService继承了GenericConversionService类。

GenericConversionService.java

protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
	//先从缓存中获取
	ConverterCacheKey key = new ConverterCacheKey(sourceType, targetType);
	GenericConverter converter = this.converterCache.get(key);
	if (converter != null) {
		return (converter != NO_MATCH ? converter : null);
	}
	//从默认的列表中获取
	converter = this.converters.find(sourceType, targetType);
	if (converter == null) {
		//默认列表中获取不到,则获取默认的转换器
		converter = getDefaultConverter(sourceType, targetType);
	}

	if (converter != null) {
		//保存到缓存中
		this.converterCache.put(key, converter);
		return converter;
	}

	this.converterCache.put(key, NO_MATCH);
	return null;
}

那我们看看下图
在这里插入图片描述
this.converters中没有从Integer转换成Integer的转换器,那么只能获取默认的转化器了。
我们好奇一下,this.converters这里的默认值是哪里来的呢?
converters这个对象是在GenericConversionService类中定义的。什么时候加进去的呢?我们在addConverter方法中打一个断点,发布是在new DefaultConversionService()方法中加入的默认转换器。
addConverter

在这里插入图片描述
默认转换器竟然是 new NoOpConverter(“NO_OP”) ,那我们继续跟进代码。

ConversionUtils.java

public static Object invokeConverter(GenericConverter converter, Object source, TypeDescriptor sourceType,
		TypeDescriptor targetType) {
	try {
		//根据转换器调用convert方法
		return converter.convert(source, sourceType, targetType);
	}
	catch (ConversionFailedException ex) {
		throw ex;
	}
	catch (Exception ex) {
		throw new ConversionFailedException(sourceType, targetType, source, ex);
	}
}

NoOpConverter.java

private static class NoOpConverter implements GenericConverter {

	private final String name;

	public NoOpConverter(String name) {
		this.name = name;
	}

	@Override
	public Set getConvertibleTypes() {
		return null;
	}

	@Override
	public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
		return source;
	}

	@Override
	public String toString() {
		return this.name;
	}
}

到这里,我们终于明白NoOpConverter类的意义了,就是你原来是什么,就返回什么吧。

到这里,我们终于完成了 (21 + 3 ) * 4 的解析了。那来总结一下吧。
1.分词:将被解析的字符串分成 ( , 21 , + , 3 , ) , * , 4
2.将分词表达式解析成对象树:21 + 3 解析成OpPlus对象 , 4 解析成 IntLiteral对象 ,再一起组成 OpMultiply对象。
3.通过getValue计算值: 先调用左节点 OpPlus的getValueInternal方法计算21 + 3 = 24 并返回 TypedValue 对象,再调用OpMultiply 的getValueInternal 计算出 24 * 4 并返回TypedValue 。
4. 将对象值转换成用户传入类型,如果找不到转换器,直接返回计算结果值。

本人经验,在解读Spring源码时,自己打断点调试。才能得出真理。

本文的github地址是
https://github.com/quyixiao/spring_tiny/blob/master/src/main/java/com/spring_101_200/test_111_120/test_113_conversion/spel/TestSpel.java

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值