彻底搞懂 Spring spel 表达式(上)
彻底搞懂 Spring spel 表达式(上)
在学习Spring spel表达式之前我们先实现这样一个需求:
定义一个 字符串 "(HelloWorld).concat(#end)", end 是一个任意字符串的变量,如果 end = " 我是拼接值",那么这个字符串的最终执行结果就是 "HelloWorld 我是拼接值"。
当然也可以直接使用String.concat,那么我们不想直接使用的这个方式,我们想动态的执行这个字符串完成拼接,而不是直接运行String的concat方法。
现在屡一下思路:
- 这里原始值:HelloWorld 。
- 要执行的方法:concat 。
- 要拼接的值得变量 : end 。
这个三个结构要素,如果想完成动态执行需要如下步骤:
- 解析字符串,获取到三个结构要素。
- 将concat变成真正可执行的方法,也就是String.concat()。
- 定义一个上下文context,用于存储end变量的值。
有了思路就来实现
1.定义存储三个结构要素的类:
public class Token { public int getStartPos() { return startPos; } public void setStartPos(int startPos) { this.startPos = startPos; } public int getEndPos() { return endPos; } public void setEndPos(int endPos) { this.endPos = endPos; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } public String getType() { return type; } public void setType(String type) { this.type = type; } private int startPos; private int endPos; private String value; private String type; }
2.定义解析字符串的接口和实现类:
public interface ExpressionParser { MapparseExpression(Token token); } public class SpelExpressionParser implements ExpressionParser { private String expressionString; private Map token= new HashMap(); public SpelExpressionParser(String expressionString){ this.expressionString = expressionString; } int s = 0; // "('HelloWorld').concat(#end)" @Override public Map parseExpression(Token targ) { char[] chars = expressionString.toCharArray(); int length = chars.length; if(null == targ){ for(int t=s; t < length; t++){ if('(' == (chars[t])){ Token token = new Token(); token.setStartPos(t+1); token.setType("value"); parseExpression(token); break; } } }else if("value".equals(targ.getType())){ for(int t=s; t < length; t++){ if(')' == (chars[t])){ targ.setEndPos(t); token.put("value",targ); } if('.' == (chars[t])){ s = t+1; Token token = new Token(); token.setStartPos(t+1); token.setType("method"); parseExpression(token); break; } } }else if("method".equals(targ.getType())){ for(int t=s; t < length; t++){ if('(' == (chars[t])){ targ.setEndPos(t); token.put("method",targ); Token token = new Token(); s = t+1; token.setType("variable"); parseExpression(token); break; } } }else if("variable".equals(targ.getType())){ for(int t=s; t < length; t++){ if('#' == (chars[t])){ targ.setStartPos(t+1); } if(')' == (chars[t])){ targ.setEndPos(t); token.put("variable",targ); break; } } } return token; } }
3.定义存储变量的上下文context:
public class EvaluationContext { private final static ThreadLocal
4.定义将concat变成真正可执行的方法:
/** * 匹配方法 * @param methods * @param name * @return */ public Method getMethod(Setmethods, String name){ for (Method v : methods) { if(v.getName().equals(name)){ //System.out.println(v.getName()); return v; } } throw new IllegalArgumentException("方法不合法"); } /** * 获取类型 * @param arg * @return */ public Class<?> getArgType(Object arg){ Class<?> type = (arg instanceof Class ? (Class<?>) arg : arg.getClass()); return type; } /** * 转换 * @param argType * @param targetObject * @return */ public Set getMethods(Class<?> argType, Object targetObject){ Set result = new LinkedHashSet<>(); Method[] methods = getMethods(argType); for (Method method : methods) { if (isCandidateForInvocation(method, argType)) { result.add(method); } } return result; } /** * 根据类型获取可用方法 * @param type * @return */ Method[] getMethods(Class<?> type) { return type.getMethods(); } boolean isCandidateForInvocation(Method method, Class<?> targetClass) { return true; }
5. 执行测试
String a = "(HelloWorld).concat(#end)"; ltd.newbee.mall.test.spel.speltest.EvaluationContext.put("end"," 我是拼接值"); ltd.newbee.mall.test.spel.speltest.ExpressionParser parser = new ltd.newbee.mall.test.spel.speltest.SpelExpressionParser(a); Maptokens = parser.parseExpression(null); Token value = tokens.get("value"); Token method = tokens.get("method"); Token vbs = tokens.get("variable"); String es = a.substring(value.getStartPos(),value.getEndPos()); String me = a.substring(method.getStartPos(),method.getEndPos()); String key = a.substring(vbs.getStartPos(),vbs.getEndPos()); Class<?> argType = getArgType(a); Set methods = getMethods(argType, es); methodToInvoke = getMethod(methods, me); Object result = methodToInvoke.invoke(es, ltd.newbee.mall.test.spel.speltest.EvaluationContext.get(key)); System.out.println(result);
看看执行结果:
HelloWorld 我是拼接值
Process finished with exit code 0
是不是很简单,理解了这部分,在了解Spring spel表达式就容易了。