// 解析表达式
public class SpelExpressionParser extends TemplateAwareExpressionParser {
public SpelExpressionParser() {
this.configuration = new SpelParserConfiguration();
}
public Expression parseExpression(String expressionString, @Nullable ParserContext context) throws ParseException {
// 是否存在模板解析
if (context != null && context.isTemplate()) {
// 解析模板
return parseTemplate(expressionString, context) {
// 如果没有表达式
if (expressionString.isEmpty()) {
// 返回一个直接的表达式,不含El表达式
return new LiteralExpression("");
}
// 解析表达式
Expression[] expressions = parseExpressions(expressionString, context) {
// 解析出来的表达式
List<Expression> expressions = new ArrayList<>();
// 获取设置的模板前缀
String prefix = context.getExpressionPrefix();
// 获取设置的模板后缀
String suffix = context.getExpressionSuffix();
// 解析字符串
int startIdx = 0;
while (startIdx < expressionString.length()) {
// 获取#{}的位置
int prefixIndex = expressionString.indexOf(prefix, startIdx);
// 找到了#{,并且的位置在最前面或者后面都行
if (prefixIndex >= startIdx) {
// #{前面还有字符串,表示前面还有表达式,表示一个具体的值,例如: 100 - #{100-200}
if (prefixIndex > startIdx) {
// 将 100- 这个表达式保存成LiteralExpression表达式对象
// LiteralExpression表示这一个直接的表达式,不需要解析变量啥的
expressions.add(new LiteralExpression(expressionString.substring(startIdx, prefixIndex)));
}
// 获取#{后面的表达式
int afterPrefixIndex = prefixIndex + prefix.length();
// 解析后缀的}的索引它的索引用来截取就是所有#{}内部的表达式
int suffixIndex = skipToCorrectEndSuffix(suffix, expressionString, afterPrefixIndex) {
// #{的位置
int pos = afterPrefixIndex;
// # 需要解析字符串的长度
int maxlen = expressionString.length();
// 解析}的位置
int nextSuffix = expressionString.indexOf(suffix, afterPrefixIndex);
// 如果不存在},表示}缺失,表达式异常
if (nextSuffix == -1) {
return -1;
}
// 创建一个栈
Deque<Bracket> stack = new ArrayDeque<>();
while (pos < maxlen) {
// 如果是}结束位置,结束
if (isSuffixHere(expressionString, pos, suffix) && stack.isEmpty()) {
break;
}
// 一一获取字符串
char ch = expressionString.charAt(pos);
switch (ch) {
case '{':
case '[':
case '(':
// 起始括号,入栈,为了校验括号对是否合法
stack.push(new Bracket(ch, pos));
break;
case '}':
case ']':
case ')':
// 栈为空,表示不存在起始括号,现又有结束括号,表示括号对不匹配
if (stack.isEmpty()) {
throw new ParseException(expressionString, pos);
}
// 出栈起始括号,因为括号对是栈,先进后出的,例如: { [ ( } ] )
Bracket p = stack.pop();
// 判断是不是与起始括号匹配匹配的括号对
if (!p.compatibleWithCloseBracket(ch)) {
throw new ParseException(expressionString, pos, "Found closing '" + ch);
}
break;
case '\'':
case '"':
// 如果是字符串,直接解析"后面的字符串,看是否还存在"
int endLiteral = expressionString.indexOf(ch, pos + 1);
// 如果后面不存在",表示""对不匹配
if (endLiteral == -1) {
throw new ParseException(expressionString, pos);
}
// 将最后的"位置重新赋值,因为""内部的位置没必要解析
// 我们现在只是为了获取最后一个}的位置,然后同时校验
pos = endLiteral;
break;
}
// 如果不是上面的符号,直接跳过
pos++;
}
// 如果字符串解析完毕,栈中还存在未出站的符号,表示括号对异常
if (!stack.isEmpty()) {
Bracket p = stack.pop();
throw new ParseException(expressionString, p.pos, "Missing closing");
;
}
// 所有字符串都处理完了,还没有找到最后一个}的位置,表示有问题
if (!isSuffixHere(expressionString, pos, suffix)) {
return -1;
}
// 返回找到的最后一个{的位置
return pos;
}
// -1表示解析出现异常
if (suffixIndex == -1) {
throw new ParseException(expressionString);
}
// 如果最后一个}的位置和#{的位置是一样的,表示#{}空的,抛出异常
if (suffixIndex == afterPrefixIndex) {
throw new ParseException(expressionString);
}
// 解析#{到}中间的表达式
String expr = expressionString.substring(prefixIndex + prefix.length(), suffixIndex);
// 去除空格
expr = expr.trim();
// 如果#{}之间全是空格,也报错
if (expr.isEmpty()) {
throw new ParseException(expressionString);
}
// 解析#{}内部的表达式,并保存
expressions.add(doParseExpression(expr, context) {
return new InternalSpelExpressionParser(this.configuration).doParseExpression(expressionString, context)
{
this.expressionString = expressionString;
// 创建表达式分割器
Tokenizer tokenizer = new Tokenizer(expressionString);
// 使用表达式进行分割
this.tokenStream = tokenizer.process() {
// 将字符串一一判断
while (this.pos < this.max) {
// 获取正在解析的字符串
char ch = this.charsToProcess[this.pos];
// 是否是字符符号
// ('a'..'z'|'A'..'Z'|'_'|'$') ('a'..'z'|'A'..'Z'|'_'|'$'|'0'..'9'|DOT_ESCAPED)*
if (isAlphabetic(ch)) {
// 记录当前字符为标识符
lexIdentifier() {
// 从当前位置开始解析
int start = this.pos;
do {
this.pos++;
}
// ('a'..'z'|'A'..'Z'|'_'|'$') ('a'..'z'|'A'..'Z'|'_'|'$'|'0'..'9'|DOT_ESCAPED)*
// 这个if就是判断字母(变量名的)
while (isIdentifier(this.charsToProcess[this.pos]));
// 符合字母的数组,也就是单词的数组
char[] subarray = subarray(start, this.pos);
// 两个字母,处理gt,lt,eq,div,not ... 特殊的两三个字母
if ((this.pos - start) == 2 || (this.pos - start) == 3) {
// 全转换为小写
String asString = new String(subarray).toUpperCase();
// 二分查找{"DIV", "EQ", "GE", "GT", "LE", "LT", "MOD", "NE", "NOT"};
// 是不是这些标识符
int idx = Arrays.binarySearch(ALTERNATIVE_OPERATOR_NAMES, asString);
// 如果是
if (idx >= 0) {
// 保存这个单词是一个操作符
pushOneCharOrTwoCharToken(TokenKind.valueOf(asString), start, subarray)
{
this.tokens.add(new Token(kind, data, pos, pos + kind.getLength()));
}
return;
}
}
// 如果不是上面的两三个特殊字符,标记这是个标识符(变量)
this.tokens.add(new Token(TokenKind.IDENTIFIER, subarray, start, this.pos));
}
}
// 如果不是字符符号,那就是操作符
else {
switch (ch) {
// 下面只贴部分操作符,实际上还有很多
// 详见org.springframework.expression.spel.standard.Tokenizer
// 下面方法都是只做一件事,将表达式封装成Token对象,描述了该表达式是字符还是操作符
// 以及字符的长度,起始位置和结束位置,方便获取这个单词或者操作符的长度
// this.tokens.add(new Token(TokenKind.LITERAL_INT, data, start, end));
case '+':
// 可能出现++,或者+
if (isTwoCharToken(TokenKind.INC)) {
pushPairToken(TokenKind.INC);
} else {
pushCharToken(TokenKind.PLUS);
}
break;
case ':':
pushCharToken(TokenKind.COLON);
break;
case '*':
pushCharToken(TokenKind.STAR);
break;
case '/':
pushCharToken(TokenKind.DIV);
break;
case '%':
pushCharToken(TokenKind.MOD);
break;
pushCharToken(TokenKind.LSQUARE);
break;
case '#':
pushCharToken(TokenKind.HASH);
break;
// 如果是\,直接抛异常
case '\\':
raiseParseException(this.pos, SpelMessage.UNEXPECTED_ESCAPE_CHAR);
break;
default:
throw new IllegalStateException("Cannot handle (" + (int) ch + ") '" + ch + "'");
}
}
}
// 返回所有分词后的对象
return this.tokens;
}
// 递归解析所有的表达式,封装成一个完整的SpelNodeImpl表达式
// 而SpelNodeImpl是对应了多种类型实现,例如+,-,*,对应的实现有OpPlus,OpAnd,OpOr
// 还有对应的值,1,2 false,"dd"对应的实现LongLiteral,XXxLiteral
// 以及赋值符号 = Assign
// 内部通过children来表示所有的子表达式,并且还包含对应的操作符,或者对应的实际值...
SpelNodeImpl ast = eatExpression();
// 封装成一个Spel表达式对象
return new SpelExpression(expressionString, ast, this.configuration);
}
});
startIdx = suffixIndex + suffix.length();
} else {
// 到这里表示所有含EL表达式的的部分解析完成
// 如果进到这里,表示除了EL表达式之外,后面还有一点表达式,就将后面的整个表达式作为一个整体
// 包装成一个直接的LiteralExpression表达式
expressions.add(new LiteralExpression(expressionString.substring(startIdx)));
// 终止while循环
startIdx = expressionString.length();
}
}
// 返回解析到的所有表达式
return expressions.toArray(new Expression[0]);
}
// 只有一个,返回
if (expressions.length == 1) {
return expressions[0];
} else {
// 如果有多个,需要将表达式进行合并
return new CompositeStringExpression(expressionString, expressions);
}
}
}
// 如果没有设置解析的模版上下文,默认不处理EL表达式模版,只能解析变量,不能使用#{表达式},因为不会解析
else {
return doParseExpression(expressionString, context);
}
}
}
// 获取表达式的值
public class SpelExpression implements Expression {
// 解析出来的el表达式
private final String expression;
// 解析出来的所有表达式的节点
private final SpelNodeImpl ast;
// 解析器配置
private final SpelParserConfiguration configuration;
// 整个计算过程的上下文对象
private EvaluationContext evaluationContext;
// 获取值
public Object getValue(EvaluationContext context) throws EvaluationException {
// 如果该表达式已经被编译了
if (this.compiledAst != null) {
// 这个提高效率的,就是jvm编译指令
return this.compiledAst.getValue(context.getRootObject().getValue(), context);
}
// 创建表达式的状态对象
ExpressionState expressionState = new ExpressionState(context, this.configuration);
// 解析表达节点中的值
Object result = this.ast.getValue(expressionState) {
// 如果变量名上this
if (this.name.equals(THIS)) {
return state.getActiveContextObject() {
// 如果不存在contextObjects,直接返回传递的rootObject
if (CollectionUtils.isEmpty(this.contextObjects)) {
return this.rootObject;
}
return this.contextObjects.element();
}
}
// 如果变量名是root
if (this.name.equals(ROOT)) {
// 直接返回传递的rootObject
TypedValue result = state.getRootContextObject() {
public TypedValue getRootContextObject () {
return this.rootObject;
}
}
return result;
}
// 其他变量,从上下文中的变量中找
TypedValue result = state.lookupVariable(this.name) {
// final EvaluationContext relatedContext;
// 从计算上下文中获取属性的值
Object value = this.relatedContext.lookupVariable(name) {
return this.variables.get(name);
}
return (value != null ? new TypedValue(value) : TypedValue.NULL);
}
// 返回获取到的变量值
Object value = result.getValue();
return result;
}
// 判断是否需要缓存编译指令
checkCompile(expressionState);
return result;
}
}
Spring中spel表达式是解析的原理
于 2024-03-14 20:26:18 首次发布