本文介紹使用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
包下。