Apache Commons Digester 二(规则模块绑定-RulesModule、异步解析-asyncParse、xml变量Substitutor、带参构造方法)
前言
上一篇对Digester做了基本介绍,也已经了解了Digester的基本使用方法,接下来将继续学习其相关特性,本篇主要涉及以下几个内容:
- 规则模块绑定,通过定义一个RulesModule接口实现类来完成规则的预先绑定,运行时重复使用
- 异步解析xml
- 解析xml中的变量,如${sys.user}
- 使用带参数的构造方法创建对象,参数来自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" ) ) );
...
} }
很显然这样做是没有错误的(其实,个人觉得还不如直接写一个私有方法,添加规则,哈哈),但是有如下缺点:
- RuleSet实际上并不是配置,只是给digester绑定下规则而已;
- digester对象与客户端耦合度比较高,直接由客户端创建;
- 每次解析调用前,都需要重复绑定规则
- 规则绑定的时候,语义性很差,可读性不好;
那么,最佳实践是什么呢,答案是使用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") ) );
...
} }
好处显而易见:
- RulesModule规则绑定的API语义化很强,使用简便,可读性高;
- 规则绑定的配置移到了启动阶段来完成;
- 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
Apache Commons Digester 二(规则模块绑定-RulesModule、异步解析-asyncParse、xml变量Substitutor、带参构造方法)的更多相关文章
- Apache Commons Digester 一 (基础内容、核心API)
前言 在许多需要处理XML格式数据的应用环境中, 如果能够以“事件驱动”的方式来处理XML文档,比如,当识别出特定的XML元素时,触发“创建对象”操作事件(或者触发调用对象的方法事件),这对于应用程序 ...
- Apache Commons Digester 三(规则注解)
前言 Digester规则的定义除了可以在代码中直接new规则添加到 Digester对象外,还可以用xml配置规则,如下所示: <digester-rules> <pattern ...
- NullPointerException org.apache.commons.digester.Digester.getXMLReader(Digester.java:1058)
http://pwu-developer.blogspot.com/2010/01/nullpointerexception.html Maven is great build tool making ...
- Apache Commons Beanutils 二 (动态Bean - DynaBeans)
相关背景 上一篇介绍了PropertyUtils的用法,PropertyUtils主要是在不修改bean结构的前提下,动态访问bean的属性: 但是有时候,我们会经常希望能够在不定义一个Java类的前 ...
- 从零开始玩转JMX(四)——Apache Commons Modeler & Dynamic MBean
Apache Commons Modeler 前面的Model MBean的创建方式看上去特别复杂,一个简单功能的类ModelMBeanUtils 写了很多代码,那有木有简单点的方式呢,答案是肯定的, ...
- 浩哥解析MyBatis源码(十二)——binding绑定模块之MapperRegisty
原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6758456.html 1.回顾 之前解析了解析模块parsing,其实所谓的解析模块就是为 ...
- MyBatis源码解析(十二)——binding绑定模块之MapperRegisty
原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6758456.html 1.回顾 之前解析了解析模块parsing,其实所谓的解析模块就是为 ...
- 《windows核心编程系列》二十一谈谈基址重定位和模块绑定
每个DLL和可执行文件都有一个首选基地址.它表示该模块被映射到进程地址空间时最佳的内存地址.在构建可执行文件时,默认情况下链接器会将它的首选基地址设为0x400000.对于DLL来说,链接器会将它的首 ...
- 一篇关于apache commons类库的详解
1.1. 开篇 在Java的世界,有很多(成千上万)开源的框架,有成功的,也有不那么成功的,有声名显赫的,也有默默无闻的.在我看来,成功而默默无闻的那些框架值得我们格外的尊敬和关注,Jakarta C ...
随机推荐
- Ubuntu部署可视化爬虫Portia2.0环境以及入门
http://www.cnblogs.com/kfpa/p/Portia.html http://brucedone.com/archives/986
- C++学习札记(3)
一边听着许巍的音乐,一遍学习着C++的精髓,这感觉这酸爽,我一个人体会和知道. 许巍是两代人共同的时代标志,他的音乐作品脍炙人口,堪称经典,经久不衰:此时此刻品味,依然有丰富的各种味道和感情.可能因为 ...
- boost asio 学习(六) 定时器
http://www.gamedev.net/blog/950/entry-2249317-a-guide-to-getting- started-with-boostasio?pg=7 6 定时器 ...
- uni-app开发一次,覆盖多端的前端框架
uni-app 是一个使用 Vue.js 开发跨平台应用的前端框架,开发者编写一套代码,可编译到iOS.Android.H5.小程序等多个平台. 一套代码,运行多个平台 uni-app实现了一套代码, ...
- Knockout.js快速学习笔记
原创纯手写快速学习笔记(对官方文档的二手理解),更推荐有时间的话读官方文档 框架简介(Knockout版本:3.4.1 ) Knockout(以下简称KO)是一个MVVM(Model-View-Vie ...
- class反射
1.获取类的方式: //第一种方式: Class c1 = Class.forName(User); //第二种方式: //java中每个类型都有class 属性. Class c2 = User.c ...
- java安全与加解密
1 安全 安全性相关内容分为认证.授权和审计(发现安全问题时可以查看相关历史记录) 用户认证 java API表示主体的是javax.security.auth.Subject类型,表示用户身份标识的 ...
- 利用fork创建子进程
创建如图的进程: #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<wait. ...
- iOS编码规范(简版)
1. 总体指导原则 [规则1-1]首先是为人编写程序,其次才是计算机. 说明:这是软件开发的基本要点,软件的生命周期贯穿产品的开发.测试.生产.用户使用.版本升级和后期维护等长期过程,只有易读.易维护 ...
- Android开发 - 掌握ConstraintLayout(三)编辑器
从本篇博客开始我们开始介绍如何使用ConstraintLayout. 既然ConstraintLayout叫约束布局,首先我们先介绍什么叫约束(Constraints): 约束(Constraints ...