zoukankan      html  css  js  c++  java
  • Apache Commons Digester 二(规则模块绑定-RulesModule、异步解析-asyncParse、xml变量Substitutor、带参构造方法)

    前言

    上一篇对Digester做了基本介绍,也已经了解了Digester的基本使用方法,接下来将继续学习其相关特性,本篇主要涉及以下几个内容:

    1. 规则模块绑定,通过定义一个RulesModule接口实现类来完成规则的预先绑定,运行时重复使用
    2. 异步解析xml
    3. 解析xml中的变量,如${sys.user}
    4. 使用带参数的构造方法创建对象,参数来自xml节点数据

    规则模块预先绑定 - RulesModule接口

    在此之前,我们使用Digester的基本流程都是每次在程序运行时绑定规则,然后解析;

    事实上,我们可以改变Digester的解析流程,启动的时候预先定义规则集,然后在运行的时候重复使用预先定义的规则;

    可能这样说比较空泛,可以看一下如下一个Web应用场景,应该就会有一个比较深刻的理解了;

    servlet场景例子

    熟悉Web开发的应该都知道servlet了,这里就不细说了,假设有一个EmployeeServlet,如下所示:

    由于servlet是单例的,而且Digester不是线程安全的,所以我们会在每次请求的的时候,new出一个Digester对象,来保证线程安全,写法如下:

    public class EmployeeServlet
      extends HttpServlet
    {
    
        public void doGet(HttpServletRequest req, HttpServletResponse res)
            throws ServletException, IOException
        {
            Digester digester = new Digester();
            digester.setNamespaceAware( true );
            digester.setXIncludeAware( true );
            digester.addObjectCreate( "employee", Employee.class );
            digester.addCallMethod( "employee/firstName", "setFirstName", 0 );
            digester.addCallMethod( "employee/lastName", "setLastName", 0 );
    
            digester.addObjectCreate( "employee/address", Address.class );
            digester.addCallMethod( "employee/address/type", "setType", 0 );
            digester.addCallMethod( "employee/address/city", "setCity", 0 );
            digester.addCallMethod( "employee/address/state", "setState", 0 );
            digester.addSetNext( "employee/address", "addAddress" );
    
            Employee employee = digester.parse( openStream( req.getParameter( "employeeId" ) ) );
            ...
    }

    我们可以很容易发现以上程序的缺点:代码没有复用,每次请求都需重复绑定规则;
    不过,我们可以使用RuleSet来解决代码没有复用的问题,如下所示,定义一个EmployeeRuleSet规则集实现RuleSet接口:

    public class EmployeeRuleSet
      implements RuleSet
    {
    
        public void addRuleInstances( Digester digester )
        {
            digester.addObjectCreate( "employee", Employee.class );
            digester.addCallMethod( "employee/firstName", "setFirstName", 0 );
            digester.addCallMethod( "employee/lastName", "setLastName", 0 );
    
            digester.addObjectCreate( "employee/address", Address.class );
            digester.addCallMethod( "employee/address/type", "setType", 0 );
            digester.addCallMethod( "employee/address/city", "setCity", 0 );
            digester.addCallMethod( "employee/address/state", "setState", 0 );
            digester.addSetNext( "employee/address", "addAddress" );
        }
    
    }

    然后在servlet中这样使用:

    public class EmployeeServlet
      extends HttpServlet
    {
    
        private final RuleSet employeeRuleSet = new EmployeeRuleSet();
    
        public void doGet(HttpServletRequest req, HttpServletResponse res)
            throws ServletException, IOException
        {
            Digester digester = new Digester();
            digester.setNamespaceAware( true );
            digester.setXIncludeAware( true );
    
            employeeRuleSet.addRuleInstances( digester );
    
            Employee employee = digester.parse( openStream( req.getParameter( "employeeId" ) ) );
            ...
        }
    
    }

    很显然这样做是没有错误的(其实,个人觉得还不如直接写一个私有方法,添加规则,哈哈),但是有如下缺点:

    1. RuleSet实际上并不是配置,只是给digester绑定下规则而已;
    2. digester对象与客户端耦合度比较高,直接由客户端创建;
    3. 每次解析调用前,都需要重复绑定规则
    4. 规则绑定的时候,语义性很差,可读性不好;

    那么,最佳实践是什么呢,答案是使用RulesModule接口,帮助我们启动时预先绑定规则,然后运行的时候,重复使用预先绑定的规则即可,如下所示:

    定义一个RulesModule接口实现类:

    class EmployeeModule
        extends AbstractRulesModule
    {
    
        @Override
        protected void configure()
        {
            forPattern( "employee" ).createObject().ofType( Employee.class );
            forPattern( "employee/firstName" ).setBeanProperty();
            forPattern( "employee/lastName" ).setBeanProperty();
    
            forPattern( "employee/address" ).createObject().ofType( Address.class ).then().setNext( "addAddress");
            forPattern( "employee/address/type" ).setBeanProperty();
            forPattern( "employee/address/city" ).setBeanProperty();
            forPattern( "employee/address/state" ).setBeanProperty();
        }
    
    }

    然后在servlet这样使用:

    public class EmployeeServlet
        extends HttpServlet
    {
    
        private final DigesterLoader loader = newLoader( new EmployeeModule() )
            .setNamespaceAware( true )
            .setXIncludeAware( true );
    
        public void doGet(HttpServletRequest req, HttpServletResponse res)
            throws ServletException, IOException
        {
            Digester digester = loader.newDigester()
    
            Employee employee = digester.parse( openStream( req.getParameter("employeeId") ) );
            ...
        }
    
    }

    好处显而易见:

    1. RulesModule规则绑定的API语义化很强,使用简便,可读性高;
    2. 规则绑定的配置移到了启动阶段来完成;
    3. digester对象不是由客户端来创建,而是通过DigesterLoader创建;

    FromXmlRulesModule

    除了自己编写类实现RulesModule接口外,digester自身提供了一个FromXmlRulesModule类,就已经实现了RulesModule接口,我们可以这样使用

                DigesterLoader loader = DigesterLoader.newLoader( new FromXmlRulesModule()
                {
    
                    @Override
                    protected void loadRules()
                    {
                        loadXMLRules( DigesterLoaderMain.class.getResource( "myrule.xml" ) );
                    }
    
                } );
           ...
          Digester digester = loader.newDigester(); // myrule.xml already parsed
          ...
          Digester newDigester = loader.newDigester(); // myrule.xml won't be parsed again!

    完整例子

    假设有一个xml如下,待解析

    <employee>
        <firstName>Pi</firstName>
        <lastName>Chen</lastName>
        <address>
            <type>CITY</type>
            <city>HangZhou</city>
            <state>2</state>
        </address>
    </employee>

    开始编码,首先,定义一个RulesModule接口实现类:

    package apache.commons.digester3.example.rulesbinder.module;
    
    import org.apache.commons.digester3.binder.AbstractRulesModule;
    
    import apache.commons.digester3.example.rulesbinder.pojo.Address;
    import apache.commons.digester3.example.rulesbinder.pojo.Employee;
    /**
     * 
     * 
     * @author    http://www.cnblogs.com/chenpi/
     * @version   2017年6月5日
     */
    public class EmployeeModule extends AbstractRulesModule {
    
        @Override
        protected void configure() {
            forPattern("employee").createObject().ofType(Employee.class);
            forPattern("employee/firstName").setBeanProperty();
            forPattern("employee/lastName").setBeanProperty();
    
            forPattern("employee/address").createObject().ofType(Address.class).then().setNext("addAddress");
            forPattern("employee/address/type").setBeanProperty();
            forPattern("employee/address/city").setBeanProperty();
            forPattern("employee/address/state").setBeanProperty();
        }
    
    }

    编写客户端类:

    package apache.commons.digester3.example.rulesbinder;
    
    import java.io.IOException;
    
    import org.apache.commons.digester3.Digester;
    import org.apache.commons.digester3.binder.DigesterLoader;
    import org.xml.sax.SAXException;
    
    import apache.commons.digester3.example.rulesbinder.module.EmployeeModule;
    import apache.commons.digester3.example.rulesbinder.pojo.Address;
    import apache.commons.digester3.example.rulesbinder.pojo.Employee;
    import apache.commons.digester3.example.simpletest.ExampleMain;
    /**
     * 
     * 
     * @author    http://www.cnblogs.com/chenpi/
     * @version   2017年6月5日
     */
    public class DigesterLoaderMain {
    
        private static DigesterLoader dl = DigesterLoader.newLoader(new EmployeeModule())
                .setNamespaceAware(false);
        public static void main(String[] args) {
            try {
                
                Digester digester = dl.newDigester();
                Employee employee = digester.parse(ExampleMain.class.getClassLoader().getResourceAsStream("employee.xml"));
    
                System.out.print(employee.getFirstName() + " ");
                System.out.print(employee.getLastName() + ", ");
                for (Address a : employee.getAddressList()) {
                    System.out.print(a.getType() + ", ");
                    System.out.print(a.getCity() + ", ");
                    System.out.println(a.getState());
                }
    
            } catch (IOException e) {
    
                e.printStackTrace();
            } catch (SAXException e) {
    
                e.printStackTrace();
            }
        }
    }

    结果打印:

    Pi Chen, CITY, HangZhou, 2

    异步解析XML

    异步解析的话,直接调用asyncParse方法即可,不过需要特别注意,因为digester对象并不是线程安全的,如下是一个简单的API使用示例:

    承接上一个例子,使用同样的xml和RulesModule实现类;

    客户端类:

    package apache.commons.digester3.example.rulesbinder;
    
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
    
    import org.apache.commons.digester3.Digester;
    import org.apache.commons.digester3.binder.DigesterLoader;
    import apache.commons.digester3.example.rulesbinder.module.EmployeeModule;
    import apache.commons.digester3.example.rulesbinder.pojo.Address;
    import apache.commons.digester3.example.rulesbinder.pojo.Employee;
    import apache.commons.digester3.example.simpletest.ExampleMain;
    /**
     * 
     * @author    http://www.cnblogs.com/chenpi/
     * @version   2017年6月5日
     */
    public class AsyncParseMain {
        private static DigesterLoader dl = DigesterLoader.newLoader(new EmployeeModule())
                .setNamespaceAware(false).setExecutorService(Executors.newSingleThreadExecutor());
        public static void main(String[] args) {
            try {
                
                Digester digester = dl.newDigester();
                Future<Employee> future = digester.asyncParse(ExampleMain.class.getClassLoader().getResourceAsStream("employee.xml"));
    
                Employee employee = future.get();
                
                System.out.print(employee.getFirstName() + " ");
                System.out.print(employee.getLastName() + ", ");
                for (Address a : employee.getAddressList()) {
                    System.out.print(a.getType() + ", ");
                    System.out.print(a.getCity() + ", ");
                    System.out.println(a.getState());
                }
    
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    }

    xml变量解析-Substitutor抽象类

    这个比较简单,定义一个VariableSubstitutor实现类,用户转换属性和body中定义的变量值;

    假设有一个xml如下所示,(其中${type}为变量):

    <employee>
        <firstName>Pi</firstName>
        <lastName>Chen</lastName>
        <address>
            <type>${type}</type>
            <city>HangZhou</city>
            <state>2</state>
        </address>
    </employee>

    那么可以这样解析如上xml:

    package apache.commons.digester3.example.rulesbinder;
    
    import java.io.IOException;
    import java.util.HashMap;
    import java.util.Map;
    
    import org.apache.commons.digester3.Digester;
    import org.apache.commons.digester3.Substitutor;
    import org.apache.commons.digester3.binder.DigesterLoader;
    import org.apache.commons.digester3.substitution.MultiVariableExpander;
    import org.apache.commons.digester3.substitution.VariableSubstitutor;
    import org.xml.sax.SAXException;
    
    import apache.commons.digester3.example.rulesbinder.module.EmployeeModule;
    import apache.commons.digester3.example.rulesbinder.pojo.Address;
    import apache.commons.digester3.example.rulesbinder.pojo.Employee;
    import apache.commons.digester3.example.simpletest.ExampleMain;
    
    /**
     * 
     * 
     * @author http://www.cnblogs.com/chenpi/
     * @version 2017年6月5日
     */
    public class SubstitutionMain
    {
        private static DigesterLoader dl = DigesterLoader.newLoader(new EmployeeModule())
            .setNamespaceAware(false);
    
        public static void main(String[] args)
        {
    
            try
            {
                // set up the variables the input xml can reference
                Map<String, Object> vars = new HashMap<String, Object>();
                vars.put("user.name", "me");
                vars.put("type", "boss");
    
                // map ${varname} to the entries in the var map
                MultiVariableExpander expander = new MultiVariableExpander();
                expander.addSource("$", vars);
    
                // allow expansion in both xml attributes and element text
                Substitutor substitutor = new VariableSubstitutor(expander);
    
                Digester digester = dl.newDigester();
                digester.setSubstitutor(substitutor);
    
                Employee employee = digester
                    .parse(ExampleMain.class.getClassLoader().getResourceAsStream("employee$.xml"));
    
                System.out.print(employee.getFirstName() + " ");
                System.out.print(employee.getLastName() + ", ");
                for (Address a : employee.getAddressList())
                {
                    System.out.print(a.getType() + ", ");
                    System.out.print(a.getCity() + ", ");
                    System.out.println(a.getState());
                }
    
            }
            catch (IOException e)
            {
    
                e.printStackTrace();
            }
            catch (SAXException e)
            {
    
                e.printStackTrace();
            }
        }
    }

    带参构造方法使用示例

    简单地说,就是在使用ObjectCreateRule规则的时候,能够传递xml中的值(属性值、body值)给构造方法使用;

    如下是一个待解析的xml:

    <root>
      <bean super="false">
        <rate>9.99</rate>
      </bean>
    </root>

    那么可以这样解析:

    package apache.commons.digester3.example.rulesbinder;
    
    import java.io.IOException;
    
    import org.apache.commons.digester3.Digester;
    import org.apache.commons.digester3.ObjectCreateRule;
    import org.apache.commons.digester3.binder.DigesterLoader;
    import org.xml.sax.SAXException;
    
    import apache.commons.digester3.example.rulesbinder.module.EmployeeModule;
    import apache.commons.digester3.example.rulesbinder.pojo.Address;
    import apache.commons.digester3.example.rulesbinder.pojo.Employee;
    import apache.commons.digester3.example.rulesbinder.pojo.MyBean;
    import apache.commons.digester3.example.simpletest.ExampleMain;
    
    /**
     * 
     * 
     * @author http://www.cnblogs.com/chenpi/
     * @version 2017年6月5日
     */
    public class ConstructorParamsMain
    {
    
        public static void main(String[] args)
        {
            try
            {
    
                ObjectCreateRule createRule = new ObjectCreateRule(MyBean.class);
                createRule.setConstructorArgumentTypes(Double.class, Boolean.class);
    
                Digester digester = new Digester();
                digester.addRule("root/bean", createRule);
                digester.addCallParam("root/bean", 1, "super");
                digester.addCallParam("root/bean/rate", 0);
    
                MyBean myBean = digester.parse(ConstructorParamsMain.class.getClassLoader()
                    .getResourceAsStream("constructor-params.xml"));
    
                System.out.println(myBean.getRate());
                System.out.println(myBean.isSuper_());
    
            }
            catch (IOException e)
            {
    
                e.printStackTrace();
            }
            catch (SAXException e)
            {
    
                e.printStackTrace();
            }
        }
    }

    结果打印:

    9.99
    false

    参考资料

    http://commons.apache.org/proper/commons-digester/

    代码参考

    https://github.com/peterchenhdu/apache-commons-digester-example

  • 相关阅读:
    服务端增加WCF服务全局异常处理机制
    ASP.NET MVC异常处理方案
    [你必须知道的异步编程]C# 5.0 新特性——Async和Await使异步编程更简单
    Asp.Net Mvc的几个小问题
    C# 多线程之Thread类
    C#泛型
    c#public、private、protected、internal、protected internal
    ASP.NET并发处理
    GDB调试器的使用
    【多媒体封装格式详解】---MKV
  • 原文地址:https://www.cnblogs.com/chenpi/p/6947441.html
Copyright © 2011-2022 走看看