在网上,我看到关于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()方法中加入的默认转换器。
默认转换器竟然是 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源码时,自己打断点调试。才能得出真理。