设为首页 收藏本站
查看: 1656|回复: 0

[经验分享] Apache Commons Digester 二(规则模块绑定

[复制链接]

尚未签到

发表于 2017-12-25 15:02:20 | 显示全部楼层 |阅读模式
前言
  上一篇对Digester做了基本介绍,也已经了解了Digester的基本使用方法,接下来将继续学习其相关特性,本篇主要涉及以下几个内容:


  • 规则模块绑定,通过定义一个RulesModule接口实现类来完成规则的预先绑定,运行时重复使用
  • 异步解析xml
  • 解析xml中的变量,如${sys.user}
  • 使用带参数的构造方法创建对象,参数来自xml节点数据
规则模块预先绑定 - RulesModule接口
  在此之前,我们使用Digester的基本流程都是每次在程序运行时绑定规则,然后解析;
  事实上,我们可以改变Digester的解析流程,启动的时候预先定义规则集,然后在运行的时候重复使用预先定义的规则;
  可能这样说比较空泛,可以看一下如下一个Web应用场景,应该就会有一个比较深刻的理解了;

servlet场景例子
  熟悉Web开发的应该都知道servlet了,这里就不细说了,假设有一个EmployeeServlet,如下所示:
  由于servlet是单例的,而且Digester不是线程安全的,所以我们会在每次请求的的时候,new出一个Digester对象,来保证线程安全,写法如下:
  

public>
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>
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>
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>
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>  

  
@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>  

  
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>  
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>  
{
  
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>  
{
  

  
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

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-427895-1-1.html 上篇帖子: idea启动tomcat服务失败 SEVERE [RMI TCP Connection(3) 下篇帖子: linux分析apache日志获取最多访问的前10个IP
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表