=Start=
缘由:
整理、总结一下编写Aviator自定义函数的方法和过程,方便以后参考。
正文:
参考解答:
Aviator 除了内置的函数之外,还允许用户自定义函数,只要实现 com.googlecode.aviator.runtime.type.AviatorFunction 接口,并注册到 AviatorEvaluator 即可使用。AviatorFunction 接口十分庞大,通常来说你并不需要实现所有的方法,只要根据你的方法的参 数个数,继承 AbstractFunction 类并 override 相应方法即可。
public class TestAviator { public static void main(String[] args) { //注册函数 AviatorEvaluator.addFunction(new AddFunction()); System.out.println(AviatorEvaluator.execute("add(1, 2)")); // 3.0 System.out.println(AviatorEvaluator.execute("add(add(1, 2), 100)")); // 103.0 } } class AddFunction extends AbstractFunction { @Override public AviatorObject call(Map<String, Object> env, AviatorObject arg1, AviatorObject arg2) { Number left = FunctionUtils.getNumberValue(arg1, env); Number right = FunctionUtils.getNumberValue(arg2, env); return new AviatorDouble(left.doubleValue() + right.doubleValue()); } public String getName() { return "add"; } }
注册函数通过 AviatorEvaluator.addFunction 方法,移除可以通过 removeFunction。另外, FunctionUtils 提供了一些方便参数类型转换的方法。
如果你的参数个数不确定,可以继承 AbstractVariadicFunction 类,只要实现其中的 variadicCall 方法即可。比如我们实现一个找到第一个参数不为 null 的函数:
public class GetFirstNonNullFunction extends AbstractVariadicFunction { public AviatorObject variadicCall(Map<String, Object> env, AviatorObject... args) { if (args != null) { for (AviatorObject arg : args) { if (arg.getValue(env) != null) { return arg; } } } return new AviatorString(null); } @Override public String getName() { return "getFirstNonNull"; } }
注册后使用就可以传入不定参数了:
getFirstNonNull(1); getFirstNonNull(1,2,3,4,null,5); getFirstNonNull(a,b,c,d);
当然,同时你仍然覆写特定的 call 方法来自定义实现。
一个样例:
public class IpContainsFunction { public static void main(String[] args) { String ipBlack = "139.119.23.*,10.119.23.2/31"; String ipStr = "10.119.23.12"; Map<String, Object> env = new HashMap<>(); env.put("ipBlack", ipBlack); env.put("ipStr", ipStr); AviatorEvaluator.addFunction(new IpContains()); System.out.println(AviatorEvaluator.execute("ipContains(ipBlack, ipStr)", env)); env.put("ipStr", "10.119.23.2"); System.out.println(AviatorEvaluator.execute("ipContains(ipBlack, ipStr)", env)); } } class IpContains extends AbstractFunction { private boolean matches(String ip, String subnet) { IpAddressMatcher ipAddressMatcher = new IpAddressMatcher(subnet); return ipAddressMatcher.matches(ip); } @Override public AviatorObject call(Map<String, Object> env, AviatorObject arg1, AviatorObject arg2) { String ipBlack = FunctionUtils.getStringValue(arg1, env); String ipStr = FunctionUtils.getStringValue(arg2, env); Boolean result = null; for (String ipList: ipBlack.split(",")) { if (ipList.contains("*")) { ipList = ipList.substring(0, ipList.lastIndexOf(".")) + ".0/24"; } result = matches(ipStr, ipList); if (result) { break; } } if (result) { return AviatorBoolean.TRUE; } else { return AviatorBoolean.FALSE; } } public String getName() { return "ipContains"; } }
参考链接:
=END=
《“如何编写Aviator自定义函数”》 有 1 条评论
因为 IpAddressMatcher 对于输入的字符串格式要求很高,所以,最好在传入参数之前,做足格式校验和规范(首尾空格删除trim()/正则格式匹配match()),尽量将不符合规范的输入过滤掉;同时加入try..except避免错误输入导致的异常使程序正常运行受影响。
`
ipStr.trim();
ipList.trim();
`