Spring源码深度解析(郝佳)-学习-Spel源码解析(二)-String计算

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

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

本文的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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值