Spring(Spring Expression
Language)表达式简称SpEL表达式,该功能在Spring中实现还是比较复杂,在Spring中单独有一个模块spring-expression来实现,所以本文主要看一小部分的源码,大概知道怎么使用就行了
在AbstractBeanFactory中就有一个属性 beanExpressionResolver 会设置默认的表达式
private BeanExpressionResolver beanExpressionResolver;
在准备BeanFactory的时候设置默认值 org.springframework.context.support.AbstractApplicationContext#prepareBeanFactory
private static final boolean shouldIgnoreSpel = SpringProperties.getFlag("spring.spel.ignore");
// 设置默认 StandardBeanExpressionResolver
if (!shouldIgnoreSpel) {
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
}
// StandardBeanExpressionResolver构造器
public StandardBeanExpressionResolver(@Nullable ClassLoader beanClassLoader) {
this.expressionParser = new SpelExpressionParser(new SpelParserConfiguration(null, beanClassLoader));
}
shouldIgnoreSpel意思是还可以通过配置关闭表达式 spring.spel.ignore=false,默认开启
BeanExpressionResolver
只有一个evaluate方法 传入表达式,返回解析后的值
public interface BeanExpressionResolver {
Object evaluate(@Nullable String value, BeanExpressionContext evalContext) throws BeansException;
}
StandardBeanExpressionResolver
StandardBeanExpressionResolver作为BeanExpressionResolver的实现类,具体实现evaluate方法
省略了部分代码
private final ParserContext beanExpressionParserContext = new ParserContext() {
@Override
public boolean isTemplate() {
return true;
}
@Override
public String getExpressionPrefix() {
return "#{";
}
@Override
public String getExpressionSuffix() {
return "}";
}
};
public Object evaluate(@Nullable String value, BeanExpressionContext evalContext) throws BeansException {
// 缓存找一下
Expression expr = this.expressionCache.get(value);
if (expr == null) {
// 解析表达式 value表达式
expr = this.expressionParser.parseExpression(value, this.beanExpressionParserContext);
// 缓存起来
this.expressionCache.put(value, expr);
}
StandardEvaluationContext sec = this.evaluationCache.get(evalContext);
// 获取解析的值
return expr.getValue(sec);
}
解析表达是的接口为 ExpressionParser
public interface ExpressionParser {
Expression parseExpression(String expressionString) throws ParseException;
Expression parseExpression(String expressionString, ParserContext context) throws ParseException;
}
就是传入一个表达式,ParserContext给定表达式的规则模版,例如上面的beanExpressionParserContext就可以解析了,其中下面的三个子类都比较重要
SpelExpressionParser、InternalSpelExpressionParser、TemplateAwareExpressionParser
上面的this.expressionParser.parseExpression会进入到TemplateAwareExpressionParser#parseExpressions
TemplateAwareExpressionParser#parseExpressions
TemplateAwareExpressionParser为SpelExpressionParser的父类
// 假设 expressionString 为 #{'127.0.0.1,::1'.split(',')}
private Expression[] parseExpressions(String expressionString, ParserContext context) throws ParseException {
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) {
if (prefixIndex > startIdx) {
expressions.add(new LiteralExpression(expressionString.substring(startIdx, prefixIndex)));
}
int afterPrefixIndex = prefixIndex + prefix.length();
int suffixIndex = skipToCorrectEndSuffix(suffix, expressionString, afterPrefixIndex);
// '127.0.0.1,::1'.split(',')
String expr = expressionString.substring(prefixIndex + prefix.length(), suffixIndex);
expr = expr.trim();
// 解析表达式 InternalSpelExpressionParser#doParseExpression
expressions.add(doParseExpression(expr, context));
startIdx = suffixIndex + suffix.length();
}
else {
expressions.add(new LiteralExpression(expressionString.substring(startIdx)));
startIdx = expressionString.length();
}
}
return expressions.toArray(new Expression[0]);
}
进入InternalSpelExpressionParser#doParseExpression
InternalSpelExpressionParser#doParseExpression
protected SpelExpression doParseExpression(String expressionString, @Nullable ParserContext context)
throws ParseException {
this.expressionString = expressionString;
Tokenizer tokenizer = new Tokenizer(expressionString);
// 这一步很关键,真正解析
this.tokenStream = tokenizer.process();
this.tokenStreamLength = this.tokenStream.size();
this.tokenStreamPointer = 0;
this.constructedNodes.clear();
// 将表达式解析成SpelNodeImpl对象
SpelNodeImpl ast = eatExpression();
Assert.state(ast != null, "No node");
Token t = peekToken();
return new SpelExpression(expressionString, ast, this.configuration);
}
tokenizer.process()这一步很关键,允许出现的特殊字符对应的解析都在这
Tokenizer#process
public List<Token> process() {
while (this.pos < this.max) {
char ch = this.charsToProcess[this.pos];
if (isAlphabetic(ch)) {
lexIdentifier();
}
else {
switch (ch) {
case '+':
if (isTwoCharToken(TokenKind.INC)) {
pushPairToken(TokenKind.INC);
}
else {
pushCharToken(TokenKind.PLUS);
}
break;
case '_': // the other way to start an identifier
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)) {
pushPairToken(TokenKind.SYMBOLIC_AND);
}
else {
pushCharToken(TokenKind.FACTORY_BEAN_REF);
}
break;
case '|':
if (!isTwoCharToken(TokenKind.SYMBOLIC_OR)) {
raiseParseException(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':
// drift over white space
this.pos++;
break;
case '\'':
lexQuotedStringLiteral();
break;
case '"':
lexDoubleQuotedStringLiteral();
break;
case 0:
// hit sentinel at end of value
this.pos++; // will take us to the end
break;
case '\\':
raiseParseException(this.pos, SpelMessage.UNEXPECTED_ESCAPE_CHAR);
break;
default:
throw new IllegalStateException("Cannot handle (" + (int) ch + ") '" + ch + "'");
}
}
}
return this.tokens;
}
上面是解析可以出现的每一个字符,另外还有一个关键地方就是eatExpression(); 它会进一步的进行处理
private SpelNodeImpl eatExpression() {
// 处理运算符 or and || && + - * / ++ 等等
SpelNodeImpl expr = eatLogicalOrExpression();
return expr;
}
其中eatLogicalOrExpression();非常关键,里面一步步处理
eatLogicalOrExpression or ||
eatLogicalAndExpression and &&
eatRelationalExpression > < >= <= == 等
eatSumExpression + - ++ --
eatProductExpression * /
eatPowerIncDecExpression ^
eatUnaryExpression
eatPrimaryExpression()
eatStartNode()
maybeEatLiteral()基本数据类型开头
maybeEatParenExpression( 开头
maybeEatTypeReference T 开头 T]
maybeEatBeanReference @打头 例如 @'bean.name' &打头(FactoryBean)
maybeEatProjection ![ 集合投影 后面例子介绍
maybeEatSelection 匹配 选择所有匹配上的 ?[ 选择第一个匹配上的^[,选择最后一个匹配上的$[
maybeEatNullReference null 直接返回null
maybeEatConstructorReference new NEW]
上面源码我们看了一点点,具体SpEL表达式其实也是一个语法树,比较复杂,我们只要知道它能做什么,该怎么写就行了
通过上面我们也知道,可以运算,可以获取Bean,只可以执行方法等等,字符规范也有()or and > < . ? $等等
下面我们就简单使用一下
案例一
public class ExpressionTest {
// 解析模版
private static final ParserContext parserContext = new ParserContext() {
@Override
public boolean isTemplate() {
return true;
}
@NotNull
@Override
public String getExpressionPrefix() {
return "#{";
}
@NotNull
@Override
public String getExpressionSuffix() {
return "}";
}
};
private final static ExpressionParser parser = new SpelExpressionParser();
private static void evaluate(String text) {
evaluate(text, null);
}
private static void evaluate(String text, Object object) {
Expression expression = parser.parseExpression(text, parserContext);
System.out.println(expression.getValue(object));
}
public static void main(String[] args) {
// map
evaluate("#{{'a':1, 'b':2, 'c':3, 'd':4, 'e':5}}");
// list
evaluate("#{{1,2,3,4,5}}");
// new 一个int数组
evaluate("#{new int[]{1,2,3}}");
// String
evaluate("#{T(java.lang.String)}");
// 逻辑运行
evaluate("#{3==5}");
// lt > <
evaluate("#{3 lt 5}");
// null
evaluate("#{null}");
}
}
输出
{a=1, b=2, c=3, d=4, e=5}
[1, 2, 3, 4, 5]
[I@37f8bb67
class java.lang.String
false
true
null
定义一组数据
public class ExpressionData {
private List<Integer> emptyData;
private final List<Integer> listData = new ArrayList<>();
private final Map<String, Object> MapData = new HashMap<>();
public ExpressionData() {
for (int i = 0; i < 10; i++) {
listData.add(i);
MapData.put(i + "", i);
}
}
public List<Integer> getEmptyData() {
return emptyData;
}
public List<Integer> getListData() {
return listData;
}
public Map<String, Object> getMapData() {
return MapData;
}
}
对以上数据处理
public class ExpressionTest {
// 解析模版
private static final ParserContext parserContext = new ParserContext() {
@Override
public boolean isTemplate() {
return true;
}
@NotNull
@Override
public String getExpressionPrefix() {
return "#{";
}
@NotNull
@Override
public String getExpressionSuffix() {
return "}";
}
};
private final static ExpressionParser parser = new SpelExpressionParser();
private static void evaluate(String text) {
evaluate(text, null);
}
private static void evaluate(String text, Object object) {
Expression expression = parser.parseExpression(text, parserContext);
System.out.println(expression.getValue(object));
}
public static void main(String[] args) {
ExpressionData data = new ExpressionData();
// 构造器实例一个ExpressionData
evaluate("#{new com.shura.expression.ExpressionData()}");
// 输出listData属性的值
evaluate("#{(listData)}", data);
evaluate("#{(listData.size())}", data);
//输出mapData属性的值
evaluate("#{mapData}", data);
// 输出listData的第一个元素
evaluate("#{listData[0]}", data);
// 输出mapData的key为4的值
evaluate("#{mapData['4']}", data);
// 输出listData所有小于5的数据
evaluate("#{listData.?[#this<5]}", data);
// 输出listData小于5的第一条数据
evaluate("#{listData.^[#this<5]}", data);
// 输出listData小于5的最后一条数据
evaluate("#{listData.$[#this<5]}", data);
// 输出mapData中value小于5的所有数据
evaluate("#{mapData.?[value<5]}", data);
// 将mapData的value全部*2之后组成一个list
evaluate("#{mapData.![value * 2]}", data);
// ?. 安全导航,如果emptyData为null,那么就返回null,在一些语言比如ts里面就支持这种语法
evaluate("#{emptyData?.size()}", data);
}
}
输出
com.shura.expression.ExpressionData@533ddba
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
10
{0=0, 1=1, 2=2, 3=3, 4=4, 5=5, 6=6, 7=7, 8=8, 9=9}
0
4
[0, 1, 2, 3, 4]
0
4
{0=0, 1=1, 2=2, 3=3, 4=4}
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
null
案例三
执行方法
定义一个工具类,又一个静态方法
如果传入 8000-9000,那么将这个区间的值全部返回
public class Utils {
public static List<String> parseRange(String ports) {
String[] split = ports.split("-");
if (split.length == 2) {
List<String> list = new ArrayList<>();
for (int i = Integer.parseInt(split[0]); i <= Integer.parseInt(split[1]); i++) {
list.add(i + "");
}
return list;
}
return Collections.singletonList(ports);
}
}
调用Utils.parseRange
public class ExpressionTest {
// ... 省略部分 从案例一拷贝
public static void main(String[] args) {
evaluate("#{'127.0.0.1,::1'.split(',')}");
evaluate("#{'127.0.0.1,::1'.split(',').length}");
evaluate("#{T(com.shura.util.Utils).parseRange('8080-8090')}");
}
}
输出
[Ljava.lang.String;@67424e82
2
[8080, 8081, 8082, 8083, 8084, 8085, 8086, 8087, 8088, 8089, 8090]
案例四
获取spring中的Bean
准备两个Bean,一个是UserBean一个是UserFactoryBean
@Component
public class UserBean implements UserGenerics {
}
@Component
public class UserFactoryBean implements SmartFactoryBean<UserBean> {
@Override
public UserBean getObject() throws Exception {
return new UserBean();
}
@Override
public Class<?> getObjectType() {
return UserBean.class;
}
@Override
public boolean isEagerInit() {
return true;
}
}
@获取Bean、&获取FactoryBean
测试
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
Object evaluate1 = context.getBeanFactory().getBeanExpressionResolver().evaluate("#{@userBean}", new BeanExpressionContext(context.getBeanFactory(), null));
Object evaluate2 = context.getBeanFactory().getBeanExpressionResolver().evaluate("#{&userFactoryBean}", new BeanExpressionContext(context.getBeanFactory(), null));
System.out.println(evaluate1);
System.out.println(evaluate2);
}
输出
com.shura.beans.UserBean@56f4468b
com.shura.beans.UserFactoryBean@6cc4c815
spring的表达式就介绍到这里,大概知道有哪些关键类,表达式大概有哪些可以使用的字符,代表的含义功能是什么等等就说明已经没问题了
欢迎关注,学习不迷路!