上一篇對Digester做了基本介紹,也已經了解了Digester的基本使用方法,接下來將繼續學習其相關特性,本篇主要涉及以下幾個內容:
規則模組綁定,透過定義一個RulesModule介面實作類別來完成規則的預先綁定,運行時重複使用
非同步解析xml
解析xml中的變量,如${sys.user}
#使用帶參數的建構方法建立對象,參數來自xml節點數據
在此之前,我們使用Digester的基本流程都是每次在程式執行時綁定規則,然後解析;
事實上,我們可以改變Digester的解析流程,啟動的時候預先定義規則集,然後在運行的時候重複使用預先定義的規則;
可能這樣說比較空泛,可以看一下如下一個Web應用場景,應該就會有一個比較深刻的理解了;
熟悉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 EmployeeModuleextends AbstractRulesModule { @Overrideprotected 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 EmployeeServletextends 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創建;
#除了自己寫類別實作RulesModule介面外,digester本身提供了一個FromXmlRulesModule類別 #,就已經實作了RulesModule接口,我們可以這樣使用:
##DigesterLoader loader = DigesterLoader.newLoader( .getResource( "myrule.xml"
<employee> <firstName>Pi</firstName> <lastName>Chen</lastName> <address> <type>CITY</type> <city>HangZhou</city> <state>2</state> </address> </employee>
##
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 * @version 2017年6月5日 */public class EmployeeModule extends AbstractRulesModule { @Overrideprotected 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 * @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
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 * @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如下所示,(其中${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 * @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 referenceMap<String, Object> vars = new HashMap<String, Object>(); vars.put("user.name", "me"); vars.put("type", "boss");// map ${varname} to the entries in the var mapMultiVariableExpander expander = new MultiVariableExpander(); expander.addSource("$", vars);// allow expansion in both xml attributes and element textSubstitutor 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 * @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(); } } }
结果打印:
以上是Apache 共享消化器的詳細內容。更多資訊請關注PHP中文網其他相關文章!