009-Joran配置框架

1. Joran配置

1.1. Logback依赖Joran。Joran是个成熟、灵活和强大的配置框架。Logback模块的许多能力都只能在Joran帮助下实现。

1.2. Joran实际上是个通用的配置系统, 可以在记录系统之外使用。

1.3. logback的设置可以在配置文件里指定, 用XML格式。Joran使用模式匹配规则处理XML文件, 对匹配了指定模式的内容应用规则(rule)。Rule类通常很小很专业。在Joran里, rule由一个pattern 和一个action组成。当模式产生匹配时, 就调用action。Patten与action的关系是Joran的核心。毫不夸张地说, 用简单的模式就能处理相当复杂的需求, 精确匹配和通配符匹配可以进行精确地处理。

2. SAX还是DOM?

2.1. 由于SAX API的基于事件的架构, 基于SAX的工具不能轻易地处理向前的引用, 即引用在当前元素之后定义的元素。有循环引用的元素也是个问题。更一般地说, DOM API允

许用户在所有元素里进行搜索和往前跳。

2.2. 这种额外的灵活性最初让我们选择了DOM API作为Joran的底层解析API。经过一些试验, 很快表明, 当解释规则是用pattern和action表达时, 在解析DOM树的过程中跳到

远处的元素没有意义。Joran得到的元素只需要按照XML文件里连续的、深度优先的顺序。

2.3. Joran先是用DOM实现, 然而, 作者换成了SAX, 以便得到只有SAX API才提供的元素位置信息。位置信息让Joran能在错误发生时显示准确的行号和列号, 这对分析问题非常

有用。

2.4. 由于其高度动态的本性, Joran API没有打算用于分析包含数以千计的元素的巨大的XML文档。

3. 模式(Pattern)

3.1. Joran的模式本质上是字符串。有两种模式: 精确模式和通配符模式。

3.2. 模式"a/b"可以匹配嵌套在顶层元素<a>里的<b>元素, 不会匹配其他模式。这就是精确模式。

3.3. 通配符模式用来匹配前缀和后缀。例如, "*/a"模式匹配任何后缀是"a"的元素, 也就是XML文档里的任意<a>元素, 而不是嵌套在<a>里的元素。"a/*"模式匹配任何前缀是

<a>的元素, 也就是嵌套在<a>里的任何元素。

4. 动作(Action)

4.1. 动作继承Action类, 包含下面两个抽象方法, 略去了其他方法。

 

4.2. 因此, 每个动作必须实现begin()方法和end()方法。是否实现body()方法是可选的, 因为Action不提供任何操作。

5. RuleStore

5.1. 之前提到过, 按照匹配的模式而调用动作是Joran的一个核心概念。一个规则(rule)就是一个与模式的关联关系加一个动作。规则存放在RuleStore。

5.2. 上面说过, Joran构建于SAX API之上。当解析XML文档时, 每个元素的开始、体(body)和结束都产生对应的事件。当Joran配置器接收到这些事件时, 就会在rule store里查找与当前模式所对应的动作。例如, 对嵌套在顶级元素"A"里的B元素, 它的开始事件、体事件和结束事件的当前模式是"A/B"。当前模式是一种数据结构, 由Joran在接收和处理SAX事件时自动维护。

5.3. 当一些规则匹配了当前模式时, 精确匹配会覆盖后缀匹配, 后缀匹配覆盖前缀匹配。详情请参考SimpleRuleStore类(ch.qos.logback.core.joran.spi.SimpleRuleStore)。

6. 解释上下文(Interpretation context)

6.1. 为了让多个动作合作, begin()和end()方法的第一个参数是解释上下文。解释上下文包括一个对象堆栈、对象map、属性列表和一个对调用了动作的Joran解析器的引用。

7. HelloWorld例子

7.1. HelloWorld例子演示了使用Joran所需要的最基本的代码。每当HelloWorldAction的begin()方法被调用时就向控制台打印"Hello World"。

7.2. XML文件的解析工作由一个配置器完成。我们开发了一个非常简单的配置器"SimpleConfigurator"。

7.3. HelloWorld程序把各部分组合到了一起:

创建包含规则的ruleMap和一个context;

把模式"helloWorld"与对应的HelloWorldAction实例相关联, 从而创建一个解析规则;

创建一个 SimpleConfigutator, 参数是前面的ruleMap;

调用配置器的doConfigure方法, 把指定的XML文件作为参数;

最后一步, 打印context里的状态消息;

7.4. helloWorld.xml文件包含一个<hello-world>元素, 没有嵌套任何其他元素。

7.5. 新建一个名为JoranHelloWorld的Java工厂, 同时拷入相关jar包

7.6. 在工程李创建一个helloWorld.xml

7.7. 编写SimpleConfigurator.java

package chapters.onJoran;

import java.util.List;
import java.util.Map;
import ch.qos.logback.core.joran.GenericConfigurator;
import ch.qos.logback.core.joran.action.Action;
import ch.qos.logback.core.joran.action.ImplicitAction;
import ch.qos.logback.core.joran.spi.ElementSelector;
import ch.qos.logback.core.joran.spi.Interpreter;
import ch.qos.logback.core.joran.spi.RuleStore;

public class SimpleConfigurator extends GenericConfigurator {
	final Map<ElementSelector, Action> ruleMap;
	final List<ImplicitAction> iaList;

	public SimpleConfigurator(Map<ElementSelector, Action> ruleMap) {
		this(ruleMap, null);
	}

	public SimpleConfigurator(Map<ElementSelector, Action> ruleMap, List<ImplicitAction> iaList) {
		this.ruleMap = ruleMap;
		this.iaList = iaList;
	}

	@Override
	protected void addInstanceRules(RuleStore rs) {
		for (ElementSelector elementSelector : ruleMap.keySet()) {
			Action action = ruleMap.get(elementSelector);
			rs.addRule(elementSelector, action);
		}
	}

	@Override
	protected void addImplicitRules(Interpreter interpreter) {
		if (iaList == null) {
			return;
		}
		for (ImplicitAction ia : iaList) {
			interpreter.addImplicitAction(ia);
		}
	}
}

7.8. 编写HelloWorldAction.java

package chapters.onJoran.helloWorld;

import org.xml.sax.Attributes;
import ch.qos.logback.core.joran.action.Action;
import ch.qos.logback.core.joran.spi.InterpretationContext;

public class HelloWorldAction extends Action {
	public void begin(InterpretationContext ec, String name, Attributes attributes) {
		System.out.println("Hello World");
	}
	
	public void end(InterpretationContext ec, String name) {
	}
}

7.9. 编写HelloWorld.java

package chapters.onJoran.helloWorld;

import java.util.HashMap;
import java.util.Map;
import ch.qos.logback.core.Context;
import ch.qos.logback.core.ContextBase;
import ch.qos.logback.core.joran.action.Action;
import ch.qos.logback.core.joran.spi.ElementSelector;
import ch.qos.logback.core.util.StatusPrinter;
import chapters.onJoran.SimpleConfigurator;

public class HelloWorld {
    public static void main(String[] args) throws Exception {
        Map<ElementSelector, Action> ruleMap = new HashMap<ElementSelector, Action>();
        // 把"helloWorld"模式和HelloWorldAction关联
        ruleMap.put(new ElementSelector("helloWorld"), new HelloWorldAction());

        SimpleConfigurator simpleConfigurator = new SimpleConfigurator(ruleMap);
        // Joran内部需要一个上下文
        Context context = new ContextBase();
        // 设置上下文
        simpleConfigurator.setContext(context);
        simpleConfigurator.doConfigure("helloWorld.xml");
       
        StatusPrinter.print(context);
    }
}

7.10. 运行项目

8. 合作动作(计算器例子)

8.1. 新建一个名为JoranCalculator的Java项目, 同时添加相关jar包

8.2. 文件calculator1.xml包扩一个computation元素, 其中嵌套了一个literal元素, 内容如下:

8.3. Calculator1.java

package chapters.onJoran.calculator1;

import java.util.HashMap;
import java.util.Map;
import ch.qos.logback.core.Context;
import ch.qos.logback.core.ContextBase;
import ch.qos.logback.core.joran.action.Action;
import ch.qos.logback.core.joran.spi.ElementSelector;
import ch.qos.logback.core.util.StatusPrinter;
import chapters.onJoran.SimpleConfigurator;
import chapters.onJoran.calculator.action.ComputationAction1;
import chapters.onJoran.calculator.action.LiteralAction;

public class Calculator1 {
    public static void main(String[] args) throws Exception {
        Map<ElementSelector, Action> ruleMap = new HashMap<ElementSelector, Action>();
        ruleMap.put(new ElementSelector("computation"), new ComputationAction1());
        // 通配符模式, computation里的任何元素, 实际上我们的computation里面只有一个literal元素
        ruleMap.put(new ElementSelector("computation/*"), new LiteralAction());

        SimpleConfigurator simpleConfigurator = new SimpleConfigurator(ruleMap);
        Context context = new ContextBase();
        simpleConfigurator.setContext(context);
        simpleConfigurator.doConfigure("calculator1.xml");

        StatusPrinter.print(context);
    }
}

8.4. Calculator1.java程序里, 模式"computation"关联于"ComputationAction1"实例, 所以调用ComputationAction1实例的begin()和end()方法。

package chapters.onJoran.calculator.action;

import org.xml.sax.Attributes;
import ch.qos.logback.core.joran.action.Action;
import ch.qos.logback.core.joran.spi.InterpretationContext;
import ch.qos.logback.core.util.OptionHelper;

public class ComputationAction1 extends Action {
    public static final String NAME_ATR = "name";

    String nameStr;

    public void begin(InterpretationContext ec, String name, Attributes attributes) {
        nameStr = attributes.getValue(NAME_ATR);
    }

    public void end(InterpretationContext ec, String name) {
        if (OptionHelper.isEmpty(nameStr)) {
            
        } else {
            Integer i = (Integer) ec.peekObject();
            System.out.println("The computation named [" + nameStr + "] resulted in the value " + i);
        }
    }
}

8.5. Calculator1.java程序里, 模式"computation/*"关联于"LiteralAction"实例, 所以调用LiteralAction实例的begin()方法, 用于把computation/literal的value属性值添加至栈顶。

package chapters.onJoran.calculator.action;

import org.xml.sax.Attributes;
import ch.qos.logback.core.joran.action.Action;
import ch.qos.logback.core.joran.spi.InterpretationContext;
import ch.qos.logback.core.util.OptionHelper;

public class LiteralAction extends Action {
    public static final String VALUE_ATR = "value";

    public void begin(InterpretationContext ic, String name, Attributes attributes) {
        String valueStr = attributes.getValue(VALUE_ATR);
        
        if (OptionHelper.isEmpty(valueStr)) {
            ic.addError("The literal action requires a value attribute");
            return;
        }

        try {
        	// 往栈顶添加computation/literal的value属性值
            Integer i = Integer.valueOf(valueStr);
            ic.pushObject(i);
        } catch (NumberFormatException nfe) {
            ic.addError("The value [" + valueStr + "] could not be converted to an Integer", nfe);
            throw nfe;
        }
    }

    public void end(InterpretationContext ic, String name) {
    }
}

8.6. SimpleConfigurator.java

package chapters.onJoran;

import java.util.List;
import java.util.Map;
import ch.qos.logback.core.joran.GenericConfigurator;
import ch.qos.logback.core.joran.action.Action;
import ch.qos.logback.core.joran.action.ImplicitAction;
import ch.qos.logback.core.joran.spi.ElementSelector;
import ch.qos.logback.core.joran.spi.Interpreter;
import ch.qos.logback.core.joran.spi.RuleStore;

public class SimpleConfigurator extends GenericConfigurator {
	final Map<ElementSelector, Action> ruleMap;
	final List<ImplicitAction> iaList;

	public SimpleConfigurator(Map<ElementSelector, Action> ruleMap) {
		this(ruleMap, null);
	}

	public SimpleConfigurator(Map<ElementSelector, Action> ruleMap, List<ImplicitAction> iaList) {
		this.ruleMap = ruleMap;
		this.iaList = iaList;
	}

	@Override
	protected void addInstanceRules(RuleStore rs) {
		for (ElementSelector elementSelector : ruleMap.keySet()) {
			Action action = ruleMap.get(elementSelector);
			rs.addRule(elementSelector, action);
		}
	}

	@Override
	protected void addImplicitRules(Interpreter interpreter) {
		if (iaList == null) {
			return;
		}
		for (ImplicitAction ia : iaList) {
			interpreter.addImplicitAction(ia);
		}
	}
}

8.7. 运行输出computation/literal的value属性值

8.8. 文件calculator2.xml除了computation和literal元素, add元素对literal的value属性值作加法, multiply元素对literal的value属性值作乘法:

8.9. Calculator2.java

package chapters.onJoran.calculator2;

import java.util.HashMap;
import java.util.Map;
import ch.qos.logback.core.Context;
import ch.qos.logback.core.ContextBase;
import ch.qos.logback.core.joran.action.Action;
import ch.qos.logback.core.joran.spi.ElementSelector;
import ch.qos.logback.core.util.StatusPrinter;
import chapters.onJoran.SimpleConfigurator;
import chapters.onJoran.calculator.action.AddAction;
import chapters.onJoran.calculator.action.ComputationAction2;
import chapters.onJoran.calculator.action.LiteralAction;
import chapters.onJoran.calculator.action.MultiplyAction;

public class Calculator2 {
    public static void main(String[] args) throws Exception {
        Map<ElementSelector, Action> ruleMap = new HashMap<ElementSelector, Action>();
        ruleMap.put(new ElementSelector("*/computation"), new ComputationAction2());
        ruleMap.put(new ElementSelector("*/computation/literal"), new LiteralAction());
        ruleMap.put(new ElementSelector("*/computation/add"), new AddAction());
        ruleMap.put(new ElementSelector("*/computation/multiply"), new MultiplyAction());

        SimpleConfigurator simpleConfigurator = new SimpleConfigurator(ruleMap);
        Context context = new ContextBase();
        simpleConfigurator.setContext(context);
        simpleConfigurator.doConfigure("calculator2.xml");

        StatusPrinter.print(context);
    }
}

8.10. Calculator2.java程序里, 模式"*/computation/add"关联于"AddAction"实例, 所以调用AddAction实例的begin()方法, 用于把栈顶的2个数值作加法后把结果重新放入栈顶。

package chapters.onJoran.calculator.action;

import java.util.EmptyStackException;
import org.xml.sax.Attributes;
import ch.qos.logback.core.joran.action.Action;
import ch.qos.logback.core.joran.spi.InterpretationContext;

public class AddAction extends Action {
    public void begin(InterpretationContext ic, String name, Attributes attributes) {
        int first = fetchInteger(ic);
        int second = fetchInteger(ic);
        ic.pushObject(first + second);
    }

    int fetchInteger(InterpretationContext ic) {
        int result = 0;

        try {
            Object o1 = ic.popObject();

            if (o1 instanceof Integer) {
                result = ((Integer) o1).intValue();
            } else {
                String errMsg = "Object [" + o1 + "] currently at the top of the stack is not an integer.";
                ic.addError(errMsg);
                throw new IllegalArgumentException(errMsg);
            }
        } catch (EmptyStackException ese) {
            ic.addError(("Expecting an integer on the execution stack."));
            throw ese;
        }
        return result;
    }

    public void end(InterpretationContext ic, String name) {
    }
}

8.11. Calculator2.java程序里, 模式"*/computation/multiply"关联于"MultiplyAction"实例, 所以调用MultiplyAction实例的begin()方法, 用于把栈顶的2个数值作乘法后把结果重新放入栈顶。

package chapters.onJoran.calculator.action;

import org.xml.sax.Attributes;
import ch.qos.logback.core.joran.action.Action;
import ch.qos.logback.core.joran.spi.InterpretationContext;
import java.util.EmptyStackException;

public class MultiplyAction extends Action {
    public void begin(InterpretationContext ic, String name, Attributes attributes) {
        int first = fetchInteger(ic);
        int second = fetchInteger(ic);
        ic.pushObject(first * second);
    }

    int fetchInteger(InterpretationContext ic) {
        int result = 0;

        try {
            Object o1 = ic.popObject();

            if (o1 instanceof Integer) {
                result = ((Integer) o1).intValue();
            } else {
                String errMsg = "Object [" + o1 + "] currently at the top of the stack is not an integer.";
                ic.addError(errMsg);
                throw new IllegalArgumentException(errMsg);
            }
        } catch (EmptyStackException ese) {
            ic.addError("Expecting an integer on the execution stack.");
            throw ese;
        }
        return result;
    }

    public void end(InterpretationContext ic, String name) {
    }
}

8.12. ComputationAction2.java的作用是输出计算结果

package chapters.onJoran.calculator.action;

import java.util.Stack;
import org.xml.sax.Attributes;
import ch.qos.logback.core.joran.action.Action;
import ch.qos.logback.core.joran.spi.InterpretationContext;
import ch.qos.logback.core.util.OptionHelper;

public class ComputationAction2 extends Action {
    public static final String NAME_ATR = "name";

    Stack<String> nameStrStack = new Stack<String>();

    public void begin(InterpretationContext ec, String name, Attributes attributes) {
        String nameStr = attributes.getValue(NAME_ATR);
        nameStrStack.push(nameStr);
    }

    public void end(InterpretationContext ec, String name) {
        String nameStr = (String) nameStrStack.pop();
        
        if (OptionHelper.isEmpty(nameStr)) {
        
        } else {
            Integer i = (Integer) ec.peekObject();
            System.out.println("The computation named [" + nameStr + "] resulted in the value " + i);
        }
    }
}

8.13. 运行输出计算结果

9. 隐式动作(Implicit actions)

9.1. 到目前为止, 我们定义的规则都称为显式动作, 因为对当前元素的模式/动作关联都能在rule store找到。然而, 在高度可扩展的系统里, 组件的数量和类型都非常多, 因此为每个模式都关联显式动作会非常冗长乏味。

9.2. 同时, 即使是在高度可扩展的系统里, 仍然可以观察到重复出现的规则。假设我们可以识别这些重复规则, 我们就可以处理在logback编译期内还是未知的包含子组件的组件。

9.3. Joran维持一个隐式动作列表, 如果没有显式模式能匹配当前模式时, 就应用隐式动作。然而, 应用隐式动作并不总是适当的。在执行隐式动作前, Joran询问给定的隐式动作是否适用于当前情况。只有当动作回答"是"时, Joran配置器才会调用隐式动作。注意, 这个额外的步骤让对给定的情况使用多个或零个动作可能。

9.4. 新建一个名为JoranImplicit的Java项目, 同时添加相关jar包

9.5. 文档implicit1.xml用于演示隐私动作是如何工作的。

9.6. PrintMe程序为模式"*/foo"关联了一个NOPAction动作实例, 即所有名为"foo"的元素。如其名字所示, NOPAction的begin和end方法都为空。

package chapters.onJoran.implicit;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import ch.qos.logback.core.Context;
import ch.qos.logback.core.ContextBase;
import ch.qos.logback.core.joran.action.Action;
import ch.qos.logback.core.joran.action.ImplicitAction;
import ch.qos.logback.core.joran.spi.ElementSelector;
import ch.qos.logback.core.util.StatusPrinter;
import chapters.onJoran.SimpleConfigurator;

public class PrintMe {
    public static void main(String[] args) throws Exception {
        Map<ElementSelector, Action> ruleMap = new HashMap<ElementSelector, Action>();
        ruleMap.put(new ElementSelector("*/foo"), new NOPAction());

        List<ImplicitAction> iaList = new ArrayList<ImplicitAction>();
        iaList.add(new PrintMeImplicitAction());
        
        SimpleConfigurator simpleConfigurator = new SimpleConfigurator(ruleMap, iaList);
        Context context = new ContextBase();
        simpleConfigurator.setContext(context);
        simpleConfigurator.doConfigure("implicit1.xml");

        StatusPrinter.printInCaseOfErrorsOrWarnings(context);
    }
}

9.7. SimpleConfigurator.java

package chapters.onJoran;

import java.util.List;
import java.util.Map;
import ch.qos.logback.core.joran.GenericConfigurator;
import ch.qos.logback.core.joran.action.Action;
import ch.qos.logback.core.joran.action.ImplicitAction;
import ch.qos.logback.core.joran.spi.ElementSelector;
import ch.qos.logback.core.joran.spi.Interpreter;
import ch.qos.logback.core.joran.spi.RuleStore;

public class SimpleConfigurator extends GenericConfigurator {
    final Map<ElementSelector, Action> ruleMap;
    final List<ImplicitAction> iaList;

    public SimpleConfigurator(Map<ElementSelector, Action> ruleMap) {
        this(ruleMap, null);
    }

    public SimpleConfigurator(Map<ElementSelector, Action> ruleMap, List<ImplicitAction> iaList) {
        this.ruleMap = ruleMap;
        this.iaList = iaList;
    }

    @Override
    protected void addInstanceRules(RuleStore rs) {
        for (ElementSelector elementSelector : ruleMap.keySet()) {
            Action action = ruleMap.get(elementSelector);
            rs.addRule(elementSelector, action);
        }
    }

    @Override
    protected void addImplicitRules(Interpreter interpreter) {
        if (iaList == null) {
            return;
        }
        for (ImplicitAction ia : iaList) {
            interpreter.addImplicitAction(ia);
        }
    }
}

9.8. PrintMe程序还在其隐式动作列表里注册了一个PrintMeImplicitAction动作。PrintMeImplicitAction动作适用于所有属性"printme"为"true"的元素。PrintMeImplicitAction类继承了ImplicitAction, isApplicable方法返回true, 才会调用动作的begin()和end()方法。

package chapters.onJoran.implicit;

import org.xml.sax.Attributes;
import ch.qos.logback.core.joran.action.ImplicitAction;
import ch.qos.logback.core.joran.spi.ElementPath;
import ch.qos.logback.core.joran.spi.InterpretationContext;

public class PrintMeImplicitAction extends ImplicitAction {
    public boolean isApplicable(ElementPath elementPath, Attributes attributes, InterpretationContext ec) {
        String printmeStr = attributes.getValue("printme");
        return Boolean.valueOf(printmeStr).booleanValue();
    }

    public void begin(InterpretationContext ec, String name, Attributes attributes) {
        System.out.println("Element [" + name + "] asked to be printed.");
    }

    public void end(InterpretationContext ec, String name) {
    }
}

9.9. NOPAction.java

package chapters.onJoran.implicit;

import org.xml.sax.Attributes;
import ch.qos.logback.core.joran.action.Action;
import ch.qos.logback.core.joran.spi.InterpretationContext;

/**
 * 不做任何操作
 */
public class NOPAction extends Action {

    public void begin(InterpretationContext ec, String name, Attributes attributes) {
    }

    public void end(InterpretationContext ec, String name) {
    }
}

9.10. 对任何<foo>元素都不会调用PrintMeImplicitAction。对于其他元素, 由于没有匹配任何显式动作, 所以调用PrintMeImplicitAction的isApplicable()方法。只有对 于那些包含属性"printme"且其属性值为"true"的元素, isApplicable()方法才返回"true"。<xyz />元素没有可用的动作, 所以会生成一个内部错误消息。这个错误消息会被PrintMe程序的最后一条语句"StatusPrinter.print"打印出来。这就解释了上面的输出结果。

10. 动态新规则

10.1. Joran的NewRuleAction动作支持在解析XML文档的过程中让Joran配置器学习新规则。

10.2. 新建一个名为JoranNewRule的Java项目, 添加相关jar包

10.3. NewRuleCalculator程序使用了两条规则, 一条规则处理最顶端的元素, 第二条规则学习新规则。

package chapters.onJoran.newRule;

import java.util.HashMap;
import java.util.Map;
import ch.qos.logback.core.Context;
import ch.qos.logback.core.ContextBase;
import ch.qos.logback.core.joran.action.Action;
import ch.qos.logback.core.joran.action.NewRuleAction;
import ch.qos.logback.core.joran.spi.ElementSelector;
import ch.qos.logback.core.util.StatusPrinter;
import chapters.onJoran.SimpleConfigurator;
import chapters.onJoran.calculator.ComputationAction2;

public class NewRuleCalculator {
    public static void main(String[] args) throws Exception {
        Map<ElementSelector, Action> ruleMap = new HashMap<ElementSelector, Action>();
        ruleMap.put(new ElementSelector("*/computation"), new ComputationAction2());
        ruleMap.put(new ElementSelector("/computation/newRule"), new NewRuleAction());
 
        SimpleConfigurator simpleConfigurator = new SimpleConfigurator(ruleMap);
        Context context = new ContextBase();
        simpleConfigurator.setContext(context);
        simpleConfigurator.doConfigure("newRule.xml");

        StatusPrinter.printInCaseOfErrorsOrWarnings(context);
    }
}

10.4. NewRuleAction是logback-core的一部分, 工作原理与其他动作基本相同, 有begin()和end()方法, 每当解析器找到"new-rule"元素时被调用。当被调用时, begin()方法查找"pattern"和"actionClass"属性, 然后实例化对应的动作类, 把模式/动作的关联关系作为一条新规则加入到Joran的rule store。下面是如何在xml文件里声明新规则:

10.5. SimpleConfigurator.java、AddAction.java、ComputationAction2.java、LiteralAction.java和MultiplyAction.java根之前的例子一样。

10.6. 运行输出

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值