这里描述一下Opendia是如何进行填槽的,填槽就是设置变量,在程序语言中,有赋值语句,在这里又是如何实现的,这个是Opendial中比较重要的第三个点。

什么是填槽

槽位是一个占位符,填槽就是将上下文中变量对应的值填充进去,这个过程就叫填槽.

在opendial中使用 {} 作为占位符。

现在有一句话是,我的姓名是{userName}, 我的性别是{sex} , 这里的 {userName} 和 {sex} 就是槽位,将上下文中对应userName和sex的value进行替换就是填槽,最终结果就是,我的姓名是chenshu00, 我的性别是男。

填槽执行过程

填槽 ===> fillSolts

  • 提取槽位
    • 正则表达式的槽位,使用正则表达式进行提取
    • 函数计算的槽位,获取表达式中的参数
  • 获取上下文变量: 上下文是一个 Map<变量,变量真实值>
  • 填充槽位
    • filled = filled.replace(“{“ + slot + “}”, strval);
      1. //正则表达式提取
      2. final static Pattern slotRegex = Pattern.compile("\\{(.+?)\\}");
      3. //函数提取 searchFromDb({height},{weight})
      4. protected FunctionalTemplate(String string) {
      5. string = string.trim();
      6. //searchFromDb
      7. String funcName = string.substring(0, string.indexOf('('));
      8. function = Settings.getFunction(funcName);
      9. StringBuilder curParam = new StringBuilder();
      10. int openParams = 0;
      11. for (int i = funcName.length() + 1; i < string.length() - 1; i++) {
      12. char c = string.charAt(i);
      13. if (c == '(') {
      14. openParams++;
      15. }
      16. else if (c == ')') {
      17. openParams--;
      18. }
      19. else if (openParams == 0 && c == ',') {
      20. Template param = Template.create(curParam.toString());
      21. parameters.add(param);
      22. slots.addAll(param.getSlots());
      23. curParam = new StringBuilder();
      24. continue;
      25. }
      26. curParam.append(c);
      27. }
      28. Template param = Template.create(curParam.toString());
      29. parameters.add(param);
      30. slots.addAll(param.getSlots());
      31. }

      Opendial 提供了那些赋值功能

Opendial提供的填槽和赋值功能极大的提升了它的可用范围,不但能进行一些固定值的设置,还能进行变量填充和传递。

常规的赋值功能有

  • 设置固定变量,var name = chenshun00;
  • 设置动态变量,var name = {userName};
  • 获取外部变量,外部变量的返回值是一个 对象 (k,v对),这里的外部是一个乏指,可能是调用service方法,可能是dubbo,可能是http,万变不离其宗
    • var user = findByName({userName})
    • var name = {user.name}
    • var sex = {user.sex}

赋值执行过程

赋值执行的过程和编程语言中处理的过程有点类试,不过一个是把变量放到栈上,一个是把变量放到Map里边。

执行赋值的过程其实是一个执行既定事实的过程,key和value已经在xml中描述好了,只要把key和value中的占位符替换一下,然后把k,v映射结果放置到上下文Map中去。

  1. <rule>
  2. <case>
  3. <effect prob="1">
  4. <set var="f44" value="searchFromDb({f})"/>
  5. </effect>
  6. </case>
  7. </rule>
  8. <rule>
  9. <case>
  10. <effect>
  11. <set var="chen" value="{f44.chen}"/>
  12. <set var="shun" value="{f44.shun}"/>
  13. </effect>
  14. </case>
  15. </rule>

上边的xml描述的是有一个rule,随后执行xml中描述的effect(函数searchFromDb),然后将effect返回的数据赋值给f44, 然后执行下一个rule,{f44.chen} , 这里并没有使用到反射或者是Ognl等技术,就是一个普普通通的字符串替换,看看f44是否是一个特殊的返回值,如果是就把f44的返回值以k,v对的形式放置到上下文中.

  1. //返回值是一个hashmap (hashmap本身也是一个特殊的Object)
  2. for (String ak : av.keyset()) {
  3. //ak ==> chen
  4. //av.get(ak) ==> Map 中chen对应的value
  5. //String.format("%s.%s", k, ak) ==> k==f44===>输出结果 f44.chen=f44.get("陈") ==> f44.chen=chenshun00
  6. aggregatedValues.addPair(String.format("%s.%s", k, ak), av.get(ak));
  7. }

如果不去考虑数据存放在哪里,赋值就是把value设置给key的一个过程。

总结

这里讲述了Opendial中比较核心的填槽和赋值的过程,填槽首要要做的是把槽位提取出来,然后在执行的过程中把上下文中的数据替换掉占位符就完成了填槽的过程。

赋值和编程语言中设置变量的方式有点相同,都是将value设置给对应的key,但是存放他们的地方不一样,一个放在栈上,一个放在Map里边,都是作为容器,从容器中能找到数据即可。