本文介绍使用commons-ognl来对ongl表达式的AST树进行分析,对想要在OGNL表达式中提取出部分信息的需求有帮助。 为何用commons-ognl呢?是由于commons-ognl比原始的ognl多了一个accept方法的支持(不支持accept树)。 另外本文的方法会简单修改commons-ognl的源码来实现。java
commons-ognl在github上有仓库,可直接克隆。node
git clone https://github.com/apache/commons-ognl.git
存在一个org.apache.commons.ognl.Node
接口,此接口是全部Ognl表达式AST树中的基本接口,全部类型的节点都是org.apache.commons.ognl.SimpleNode
类的派生类。git
查看org.apache.commons.ognl.Node
接口, 包含一个accept方法,定义看图:github
查看accept方法的实现,竟只对visitor回调了当前节点,并无visit其树枝下的子节点(证明导读部分)。shell
咱们想要的是分析AST树,而不单单是独立的节点。 因此修改一下源码,java8能够直接写在Node接口中:apache
default <R, P> R acceptTree(NodeVisitor<? extends R, ? super P> visitor, P data)throws OgnlException{ R result = accept(visitor, data); for (int i = 0; i < jjtGetNumChildren(); i++) { result = jjtGetChild(i).acceptTree(visitor, data); } return result; }
添加一个adapter类NodeVisitorAdapter
app
package org.apache.commons.ognl; public class NodeVisitorAdapter<R,P> implements NodeVisitor<R,P>{ @Override public R visit(ASTSequence node, P data) throws OgnlException { return null; } @Override public R visit(ASTAssign node, P data) throws OgnlException { return null; } @Override public R visit(ASTTest node, P data) throws OgnlException { return null; } @Override public R visit(ASTOr node, P data) throws OgnlException { return null; } @Override public R visit(ASTAnd node, P data) throws OgnlException { return null; } @Override public R visit(ASTBitOr node, P data) throws OgnlException { return null; } @Override public R visit(ASTXor node, P data) throws OgnlException { return null; } @Override public R visit(ASTBitAnd node, P data) throws OgnlException { return null; } @Override public R visit(ASTEq node, P data) throws OgnlException { return null; } @Override public R visit(ASTNotEq node, P data) throws OgnlException { return null; } @Override public R visit(ASTLess node, P data) throws OgnlException { return null; } @Override public R visit(ASTGreater node, P data) throws OgnlException { return null; } @Override public R visit(ASTLessEq node, P data) throws OgnlException { return null; } @Override public R visit(ASTGreaterEq node, P data) throws OgnlException { return null; } @Override public R visit(ASTIn node, P data) throws OgnlException { return null; } @Override public R visit(ASTNotIn node, P data) throws OgnlException { return null; } @Override public R visit(ASTShiftLeft node, P data) throws OgnlException { return null; } @Override public R visit(ASTShiftRight node, P data) throws OgnlException { return null; } @Override public R visit(ASTUnsignedShiftRight node, P data) throws OgnlException { return null; } @Override public R visit(ASTAdd node, P data) throws OgnlException { return null; } @Override public R visit(ASTSubtract node, P data) throws OgnlException { return null; } @Override public R visit(ASTMultiply node, P data) throws OgnlException { return null; } @Override public R visit(ASTDivide node, P data) throws OgnlException { return null; } @Override public R visit(ASTRemainder node, P data) throws OgnlException { return null; } @Override public R visit(ASTNegate node, P data) throws OgnlException { return null; } @Override public R visit(ASTBitNegate node, P data) throws OgnlException { return null; } @Override public R visit(ASTNot node, P data) throws OgnlException { return null; } @Override public R visit(ASTInstanceof node, P data) throws OgnlException { return null; } @Override public R visit(ASTChain node, P data) throws OgnlException { return null; } @Override public R visit(ASTEval node, P data) throws OgnlException { return null; } @Override public R visit(ASTConst node, P data) throws OgnlException { return null; } @Override public R visit(ASTThisVarRef node, P data) throws OgnlException { return null; } @Override public R visit(ASTRootVarRef node, P data) throws OgnlException { return null; } @Override public R visit(ASTVarRef node, P data) throws OgnlException { return null; } @Override public R visit(ASTList node, P data) throws OgnlException { return null; } @Override public R visit(ASTMap node, P data) throws OgnlException { return null; } @Override public R visit(ASTKeyValue node, P data) throws OgnlException { return null; } @Override public R visit(ASTStaticField node, P data) throws OgnlException { return null; } @Override public R visit(ASTCtor node, P data) throws OgnlException { return null; } @Override public R visit(ASTProperty node, P data) throws OgnlException { return null; } @Override public R visit(ASTStaticMethod node, P data) throws OgnlException { return null; } @Override public R visit(ASTMethod node, P data) throws OgnlException { return null; } @Override public R visit(ASTProject node, P data) throws OgnlException { return null; } @Override public R visit(ASTSelect node, P data) throws OgnlException { return null; } @Override public R visit(ASTSelectFirst node, P data) throws OgnlException { return null; } @Override public R visit(ASTSelectLast node, P data) throws OgnlException { return null; } }
java8如下须要将acceptTree
写在SimpleNode中,使用时须要强制转换,这里就再也不单独把代码列出来了。maven
完成了改造就能够visit整颗树了,如今来了一个需求,把Ognl中全部使用到的变量都get出来。 上代码片段ide
package org.apache.commons.ognl; import org.junit.Test; import java.util.HashSet; import java.util.Set; public class AstVisitorTest { @Test public void astVisitorTest() throws OgnlException { String ognlExpr = "map1.keyv1.name=='name1'"; Node rootNode = (Node) Ognl.parseExpression(ognlExpr); Set<String> variables = new HashSet<>(); rootNode.acceptTree(new NodeVisitorAdapter<Set<String>, Set<String>>() { @Override public Set<String> visit(ASTProperty node, Set<String> data) throws OgnlException { data.add(String.valueOf(node)); return data; } }, variables); System.out.println(new StringBuilder(1 << 10).append("variables \n\t").append(variables)); } }
运行结果:ui
[map1, name, keyv1]
/properties/
<maven.compile.source>1.8</maven.compile.source> <maven.compile.target>1.8</maven.compile.target>
部分节点的访问限制为 package public
,如(org.apache.commons.ognl.ASTAnd
、org.apache.commons.ognl.ASTNot
等),须要将visitor
放在org.apache.commons.ognl
包下。