=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();
`