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

本文转自风一样的码农博客园博客,原文链接:http://www.cnblogs.com/chenpi/p/6947441.html,如需转载请自行联系原作者
相关文章
|
存储 Java 文件存储
微服务——SpringBoot使用归纳——Spring Boot使用slf4j进行日志记录—— logback.xml 配置文件解析
本文解析了 `logback.xml` 配置文件的详细内容,包括日志输出格式、存储路径、控制台输出及日志级别等关键配置。通过定义 `LOG_PATTERN` 和 `FILE_PATH`,设置日志格式与存储路径;利用 `&lt;appender&gt;` 节点配置控制台和文件输出,支持日志滚动策略(如文件大小限制和保存时长);最后通过 `&lt;logger&gt;` 和 `&lt;root&gt;` 定义日志级别与输出方式。此配置适用于精细化管理日志输出,满足不同场景需求。
3212 1
|
XML Web App开发 JavaScript
XML DOM 解析器
XML DOM 解析器
|
XML JavaScript Android开发
【Android】网络技术知识总结之WebView,HttpURLConnection,OKHttp,XML的pull解析方式
本文总结了Android中几种常用的网络技术,包括WebView、HttpURLConnection、OKHttp和XML的Pull解析方式。每种技术都有其独特的特点和适用场景。理解并熟练运用这些技术,可以帮助开发者构建高效、可靠的网络应用程序。通过示例代码和详细解释,本文为开发者提供了实用的参考和指导。
592 15
|
消息中间件 JavaScript 前端开发
最细最有条理解析:事件循环(消息循环)是什么?为什么JS需要异步
度一教育的袁进老师谈到他的理解:单线程是异步产生的原因,事件循环是异步的实现方式。 本质是因为渲染进程因为计算机图形学的限制,只能是单线程。所以需要“异步”这个技术思想来解决页面阻塞的问题,而“事件循环”是实现“异步”这个技术思想的最主要的技术手段。 但事件循环并不是全部的技术手段,比如Promise,虽然受事件循环管理,但是如果没有事件循环,单一Promise依然能实现异步不是吗? 博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您
|
XML Web App开发 JavaScript
XML DOM 解析器
XML DOM 解析器
|
XML Web App开发 JavaScript
XML DOM 解析器
XML DOM 解析器
|
XML Web App开发 JavaScript
XML DOM 解析器
XML DOM 解析器
|
数据处理 Python
深入探索:Python中的并发编程新纪元——协程与异步函数解析
深入探索:Python中的并发编程新纪元——协程与异步函数解析
218 3
|
XML Web App开发 JavaScript
XML DOM 解析器
XML DOM 解析器
|
XML Java 数据格式
手动开发-简单的Spring基于XML配置的程序--源码解析
手动开发-简单的Spring基于XML配置的程序--源码解析
380 0

推荐镜像

更多