如何编写Aviator自定义函数


=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 条评论

  1. 因为 IpAddressMatcher 对于输入的字符串格式要求很高,所以,最好在传入参数之前,做足格式校验和规范(首尾空格删除trim()/正则格式匹配match()),尽量将不符合规范的输入过滤掉;同时加入try..except避免错误输入导致的异常使程序正常运行受影响。
    `
    ipStr.trim();
    ipList.trim();
    `

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注