Java使用Aviator表达式 学习记录(十五)

2,021 阅读3分钟

这是我参与8月更文挑战的第17天,活动详情查看:8月更文挑战

自定义函数和调用 Java 方法

Function Missing

Function Missing 是类似 Ruby 的 method missing 机制,本质上是一个函数调用的兜底机制,当函数找不到的时候,就调用到 FunctionMissing 接口的实现:

/**
 * Function not found hook interface. The
 * {@link FunctionMissing#onFunctionMissing(String, Map, AviatorObject...)} method will be called
 * when function not found, return the invocation result.
 *
 * @see AviatorEvaluatorInstance#setFunctionMissing(FunctionMissing)
 * @see AviatorEvaluator#setFunctionMissing(FunctionMissing)
 * @author dennis zhuang(killme2008@gmail.com)
 * @since 4.2.5
 *
 */
public interface FunctionMissing {
  /**
   * Called when function not found, return the invocation result.
   *
   * @param name function name
   * @param env invocation env
   * @param args invocation arguments.
   * @return The invocation result.
   */
  AviatorObject onFunctionMissing(String name, Map<String, Object> env, AviatorObject... args);
}

onFunctionMissing 方法接受函数的名称 name ,调用的上下文 env ,以及参数列表 args 。

自定义的处理器可以通过 AviatorEvaluatorInstance#setFunctionMissing 来设置,一个简单的例子:

public class FunctionMissingExample {

  private static class TestFunctionMissing implements FunctionMissing {

    @Override
    public AviatorObject onFunctionMissing(final String name, final Map<String, Object> env,
        final AviatorObject... args) {
      // Returns the function name.
      System.out.println(
          "Function not found, name=" + name + ", env=" + env + ", args=" + Arrays.toString(args));
      return FunctionUtils.wrapReturn(name);
    }

  }

  public static void main(final String[] args) {
    // Set function missing handler.
    AviatorEvaluator.setFunctionMissing(new TestFunctionMissing());

    System.out.println(AviatorEvaluator.execute("test(1,2,3)"));
    System.out.println(AviatorEvaluator.execute("not_found(1,2,3)"));
  }
}

TestFunctionMissing 只是打印了三个参数,然后返回函数名称,接下来我们测试了两个例子,执行结果:

Function not found, name=test, env=com.googlecode.aviator.utils.Env@681a9515{__instance__=com.googlecode.aviator.AviatorEvaluatorInstance@3af49f1c, __env__=<this>}, args=[<Long, 1>, <Long, 2>, <Long, 3>]
test
Function not found, name=not_found, env=com.googlecode.aviator.utils.Env@13221655{__instance__=com.googlecode.aviator.AviatorEvaluatorInstance@3af49f1c, __env__=<this>}, args=[<Long, 1>, <Long, 2>, <Long, 3>]
not_found

test 和 not_found  都没有定义,打印了函数名称、上下文 env 和参数列表 args,返回结果是名称 name,符合预期。

调用 Java 实例方法(基于反射)

JavaMethodReflectionFunctionMissing 是一个特殊的 function missing 实现,它基于反射,自动调用传入的第一个参数的实例方法,将 method(instance, args...) 调用转化为 instance.method(args...) 调用:

    // 启用基于反射的方法查找和调用
    AviatorEvaluator.setFunctionMissing(JavaMethodReflectionFunctionMissing.getInstance());
    // 调用 String#indexOf
    System.out.println(AviatorEvaluator.execute("indexOf('hello world', 'w')"));
    // 调用 Long#floatValue
    System.out.println(AviatorEvaluator.execute("floatValue(3)"));
    // 调用 BigDecimal#add
    System.out.println(AviatorEvaluator.execute("add(3M, 4M)"));

这个方式提供了最大的方法调用灵活性,只要将调用的对象作为第一个参数传入,就会自动查找该对象是否拥有对应的 public 实例方法,如果有,就转为反射调用进行。

当然也存在缺陷:

  • 和导入 java 方法类似,性能相比自定义函数较差,接近 3 倍的差距,原因也是反射。
  • 无法调用静态方法,静态方法调用仍然需要采用其他两种方式。
  • 如果第一个参数为 null,无法找出方法,因为没有对象  class 信息。

函数加载器 FunctionLoader

FunctionLoader 用于定义函数加载器:

public interface FunctionLoader {

  /**
   * Invoked when function not found
   *
   * @param name function name
   */
  public AviatorFunction onFunctionNotFound(String name);
}

当函数找不到的时候,会尝试优先从函数加载器加载函数并调用,如果还没有,最终调用 FunctionMissing 。

AviatorScript 提供了两个FunctionLoader 实现。

从 classpath 加载自定义函数

  • ClassPathConfigFunctionLoader ,默认配置,用于从 classpath 加载自定义函数列表。你可以在 classpath 下放置一个配置文件 aviator_functions.config,内容是一行一行的自定义函数类的完整名称,例如:
# 这是一行注释
com.example.TestFunction
com.example.GetFirstNonNullFunction

那么 AviatorScript 将在 JVM 启动的时候自动加载这些自定义函数,配置文件中以 # 开头的行将被认为是注释。如果你想自定义文件路径,可以通过传入环境变量

-Dcom.googlecode.aviator.custom_function_config_file=xxxx.config

从 spring 容器加载自定义函数

  • SpringContextFunctionLoader ,如果你使用 spring 容器, SpringContextFunctionLoader 帮助你从 spring 容器里加载函数,按照 bean 的名称寻找:
 @Override
  public AviatorFunction onFunctionNotFound(String name) {
    try {
      return (AviatorFunction) this.applicationContext.getBean(name);
    } catch (NoSuchBeanDefinitionException e) {
      return null;
    }
  }

注意,当自定义实现加载器的实现的时候,如果找不到对应的函数,请在 onFunctionNotFound 返回 null,才会进入 FunctionMissing 实现。