师兄总结 :Commons Digester

来源:互联网 发布:新开淘宝店铺怎么引流 编辑:程序博客网 时间:2024/04/29 19:28
学习中遇到了Commons Digester,它具有把XML文件映射到Java对象的能力,下面总结一下:
 
//所要解析的XML文件userdatabase.xml
<?xml version="1.0" encoding="UTF-8"?>
<database>
   <user userName="guest" password="guest" >
   </user>
</database>
 
1.常用到的方法:
  Digester digester = new Digester();
  digester.push(this);//将类实例压入堆栈
    
 digester.addObjectCreate("database/user", "addressbook.model.UserBean");//对user节点注入对象创建规则,即在SAX的事件遭遇到user节点的时候,创建UserBean类的实例,并压入堆栈,此时堆栈中从栈底到栈顶分别为AddressBook实例,Person类实例。
   digester.addSetProperties("database/user"); //对user节点注入属性设置规则,即在SAX的事件遭遇到user节点中的Attributes时,根据属性列表中的属性值对,这儿就是useName=guest,password="guest"使用Java反射(reflection)机制,调用当前栈顶对象即UserBean实例类中useName,password属性的标准的JavaBean方法,setUserName,setPassWord.
   digester.addSetNext("database/user", "addUser");//对user节点注入父节点方法调用规则,即在SAX事件遭遇到user节点的时候,调用栈中UserBean实例的父实例中的addUser方法,UserBean实例作为addUser方法的参数。将栈顶元素作为次栈顶元素指定方法的参数.
   digester.addCallMethod("database/user", "setUserName", 0);//对user节点注入方法调用规则,调用当前栈顶对象即UserBean实例中的setUserName方法,而此方法的参数即是当前name节点的字符内容。通常这规则和addCallParam规则配合使用,这儿是一种特殊情况。
   
2.常用到的规则以及对应的方法。
     ObjectCreateRule:这个规则比较简单,此规则就是对指定的模式创建一个类的实例,并将当前实例压入堆栈,并且在遭遇元素结束时,将当前的栈顶实例弹出栈。
  对应Digester中有关这个规则的Javadoc方法说明:
      addObjectCreate(java.lang.String pattern, java.lang.Class clazz) - 方法参数说明了一切
      addObjectCreate(java.lang.String pattern, java.lang.String className) - 同上
      addObjectCreate(java.lang.String pattern, java.lang.String attributeName, java.lang.Class clazz) - 这个稍微解释一下,多了一个参数attributeName,这个参数的意思就是如果在当前匹配模式的节点中定义了属性,则默认就采用这个attributeName所对应的值来加载实例。
     addObjectCreate(java.lang.String pattern, java.lang.String className, java.lang.String attributeName) -同上
           BeanPropertySetterRule:Bean属性设置规则,对匹配当前指定模式的元素设置bean属性同名或者指定属性的值。
    对应Digester中有关这个规则的Javadoc方法说明:
      addBeanPropertySetter(java.lang.String pattern)  - 对匹配当前指定模式的元素设置bean属性同名属性的值。
      addBeanPropertySetter(java.lang.String pattern, java.lang.String propertyName)  - 对匹配当前指定模式的元素设置bean属性指定属性的值。
      
    CallMethodRule:方法调用规则,对匹配当前指定模式的元素,初始化指定的方法类型和方法参数,并压入堆栈。此规则需要和CallMethodRule
    规则配合使用:
    对应Digester中有关这个规则的Javadoc方法说明:
      addCallMethod(java.lang.String pattern, java.lang.String methodName) - 初始化指定的方法,只是当前方法不需要任何参数。
      addCallMethod(java.lang.String pattern, java.lang.String methodName, int paramCount) - 初始化指定的方法,参数个数为paramCount,参数类型缺省为java.lang.String。注意有种特殊情况,就是paramCount为0的时候,默认使用当前元素的字符数据作为参数值。
      addCallMethod(java.lang.String pattern, java.lang.String methodName, int paramCount, java.lang.Class[] paramTypes) -
      同上,只是有指定的参数类型.
      addCallMethod(java.lang.String pattern, java.lang.String methodName, int paramCount, java.lang.String[] paramTypes) -
      同上
      
    CallParamRule:提供CallMethodRule规则所需要的参数,必须跟CallMethodRule配合使用。
    对应Digester中有关这个规则的Javadoc方法说明:
      addCallParam(java.lang.String pattern, int paramIndex) - 使用匹配当前指定模式元素的字符数据作为索引为paramIndex的参数值.
      addCallParam(java.lang.String pattern, int paramIndex, boolean fromStack)  - 从操作数栈中,默认取出栈顶对象作为索引
      为paramIndex的参数值.
      addCallParam(java.lang.String pattern, int paramIndex, int stackIndex)  - 从操作数栈中,取出从栈顶数第stackIndex + 1个
      对象作为索引为paramIndex的参数值.
      addCallParam(java.lang.String pattern, int paramIndex, java.lang.String attributeName) - 使用属性attrbuteName的值作为
      索引为paramIndex的参数值.
      
    PathCallParamRule:提供当前匹配的模式路径作为方法调用所需要的参数,配合CallMethodRule使用。
    对应Digester中有关这个规则的Javadoc方法说明:
      addCallParamPath(java.lang.String pattern, int paramIndex) - 指定索引为paramIndex的值为当前匹配模式的路径.
     
    ObjectParamRule;指定对象作为指定索引的值,配合CallMethodRule使用。
    对应Digester中有关这个规则的Javadoc方法说明:
      addObjectParam(java.lang.String pattern, int paramIndex, java.lang.Object paramObj) - 指定索引为paramIndex的值为给定
      的对象的值.
      
    SetNestedPropertiesRule:当前匹配模式的直接子元素和对应bean的属性之间的映射.
    对应Digester中有关这个规则的Javadoc方法说明:
      addSetNestedProperties(java.lang.String pattern)  - 默认当前匹配模式的元素的直接子元素和bean中对应属性之间值的映射
      addSetNestedProperties(java.lang.String pattern, java.lang.String[] elementNames, java.lang.String[] propertyNames)   -
      当前匹配模式的直接子元素集和bean中属性集之间的映射
      addSetNestedProperties(java.lang.String pattern, java.lang.String elementName, java.lang.String propertyName) -
      当前匹配模式的直接子元素和bean中属性之间的映射

SetNextRule:匹配当前模式时,将栈顶对象作为次栈顶对象中指定方法的参数。
  对应Digester中有关这个规则的Javadoc方法说明:
    addSetNext(java.lang.String pattern, java.lang.String methodName) - 指定次栈顶元素的方法名称,将栈顶对象作为指定方法的参数。
    addSetNext(java.lang.String pattern, java.lang.String methodName, java.lang.String paramType) - 指定次栈顶元素的方法名称和参数类型,将栈顶对象作为指定方法的参数。

  SetPropertiesRule:匹配当前模式的元素的属性与栈顶对象中同名或者指定对应关系的属性值。
  对应Digester中有关这个规则的Javadoc方法说明:
    addSetProperties(java.lang.String pattern) - 指定栈顶对象属性的值为当前匹配元素中同名元素属性的值。
    addSetProperties(java.lang.String pattern, java.lang.String[] attributeNames, java.lang.String[] propertyNames) - 对应栈顶对象属性的值为指定的当前匹配元素中元素属性的值
    addSetProperties(java.lang.String pattern, java.lang.String attributeName, java.lang.String propertyName)  - 同上

  SetPropertyRule:不常用的一个规则,主要用于key-value值对。一个元素属性为栈顶对象的属性,一个元素属性为栈顶对象的属性的值。
  对应Digester中有关这个规则的Javadoc方法说明:
    addSetProperty(java.lang.String pattern, java.lang.String name, java.lang.String value) - name和value都是匹配当前模式的元素属性,name的值是栈顶对象中同名的属性名称,而value的值是栈顶对象中属性名称为name的值。(好像有点绕口)
SetRootRule:将当前栈顶对象作为根对象中指定方法的参数。
  对应Digester中有关这个规则的Javadoc方法说明:
    addSetRoot(java.lang.String pattern, java.lang.String methodName) - 将当前栈顶对象作为根对象中指定为methodName方法的参数。
    addSetRoot(java.lang.String pattern, java.lang.String methodName, java.lang.String paramType) -  同上,只是多了一个方法参数的类型

 SetTopRule:与SetNextRule正好想法,是将次栈顶元素作为栈顶元素指定方法的参数。
  对应Digester中有关这个规则的Javadoc方法说明:
    addSetTop(java.lang.String pattern, java.lang.String methodName) - 将次栈顶元素作为栈顶元素指定为methodName方法的参数。
    addSetTop(java.lang.String pattern, java.lang.String methodName, java.lang.String paramType) -  同上,只是多了一个方法参数的类型

1、digester的简介

    digester是struts的一个工具,用来解析struts的配置文件struts-config.xml,将xml的元素转换成java的对象。Digester是通过调用预定义的规则来操作xml元素,将xml的元素转换为java对象。

   Digester底层采用SAX解析xml文件。首先识别出特定xml元素(实际细分为begin,body,end,finish四个步骤)后,执行特定的动作。digester通过匹配模式(matching pattern)识别特定的元素,而相关的操作由rule来执行。

2、degister的创建过程。

xml文档如下:

<web-app>

<servlet-mapping>
    <servlet-name>action</servlet-name>
    <url-pattern>*.do</url-pattern>
   </servlet-mapping>
   <servlet-mapping>
    <servlet-name>ajax</servlet-name>
    <url-pattern>/ajax</url-pattern>
</servlet-mapping>

</web-app>


(1)创建一个digester的实例,一个解析(digester.parse())请求完了以后,digster还可以被后面的解析请求服用。

         Digester digester = new Digester();

   (2)设置一些配置属性(configuration properties),用于设置解析操作。

        digester.setNamespaceAware(true);
        digester.setValidating(false);

   (3)将一个或几个对象塞入Digester对象栈顶,便于解析调用。

        digester.push(this);//this是actionservlet对象。

   (4)注册xml的匹配模板(matching pattern),当一个匹配模板被输入的xml文档中识别出来,其相应的规则就被激活。当识别出该模板后,这些规则依序依次执行。

//    protected String registrations[] = {
        "-//Apache Software Foundation//DTD Struts Configuration 1.0//EN",
        "/org/apache/struts/resources/struts-config_1_0.dtd",
        "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN",
        "/org/apache/struts/resources/struts-config_1_1.dtd",
        "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN",
        "/org/apache/struts/resources/web-app_2_2.dtd",
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN",
        "/org/apache/struts/resources/web-app_2_3.dtd"
    };

//前面已经设置了registrations的值

        for (int i = 0; i < registrations.length; i += 2) {
            URL url = this.getClass().getResource(registrations[i+1]);
            if (url != null) {
                digester.register(registrations[i], url.toString());
            }
        }

(5)digeter先制定相应的rule规则,这些rule保存在digester对象里面,只有等到解析的时候,才会真正去执行。

        digester.addCallMethod("web-app/servlet-mapping",
                               "addServletMapping", 2);

        //表示xml文档碰到web-app元素下的servlet-mapping,就调用当前栈顶的对象(即actionservlet)的addServletMapping方法,并传递两个参数给该方法。当此处只是声明一个new CallMethodRule(addServletMapping, 2)的对象。

        digester.addCallParam("web-app/servlet-mapping/servlet-name", 0);

      //表示xml文档碰到web-app/servlet-mapping的servlet-name元素,就将servlet-name设的值作为addServletMapping方法的第一个参数。当此处只是声明一个new CallParamRule(0)的对象。

        digester.addCallParam("web-app/servlet-mapping/url-pattern", 1);

      //表示xml文档碰到web-app/servlet-mapping的url-pattern元素,就将url-pattern设的值作为addServletMapping方法的第二个参数

其中解析的时候会去调用begin(Attributes attributes),end(String namespace, String name)等方法,其中attributes中保存的是配置文件的内容,该类里面有个data变量是string[] 数组,存放配置的属性和配置的属性值。[, path, path, CDATA, /logon, , name, name, CDATA, logonForm, , input, input, CDATA, /WEB-INF/jsp/bas/logon.jsp, , scope, scope, (request|session), request, , type, type, CDATA, com.longtop.bas.web.LogonAction, , validate, validate, (true|false|yes|no), false]

   (6)解析xml文件,当必须传给parse()一个参数(文件引用)。

        InputStream input =
            getServletContext().getResourceAsStream("/WEB-INF/web.xml");//xml文档的引用

        try {
            digester.parse(input);//传递xml文档的引用,并且一定要捕捉并处理IOException和SAXException

        } catch (IOException e) {
            log.error(internal.getMessage("configWebXml"), e);
            throw new ServletException(e);
            
        } catch (SAXException e) {
            log.error(internal.getMessage("configWebXml"), e);
            throw new ServletException(e);

   调用parse(input)的时候,digester才真正解析xml文档,规则和相关的操作才被执行(即actionservlet的addServletMapping()才真正被执行)。如上xml文档所示的有两个action-mapping的节点,所以actionservlet类的addServletMapping方法会被调用两次。




    3、对象栈的理解

     当识别出xml元素的开始,就将该元素对应的对象压入栈顶。当识别出该元素的子节点,则又生成一个对象压入栈顶,并将该元素对应的对象作为一个参数,传递给子节点对应的对象。这样就可以建立1:1或1:N的父子关系。当解析器遇到一个xml元素的“开始”,就将该元素对应的对象压入栈顶,当解析完该元素的所有子元素,解析器遇到该元素的“结束”,就弹出该对象,并进行相关的处理。

4、digester的所有操作(规则)

   (1)首先引用一个比较完整的xml文档

<struts-config><!-- 配置数据源 -->
<data-sources >
    <data-source key="mysql" className="org.apache.struts.config.DataSourceConfig2">
      <set-property property="driverClass" value="org.gjt.mm.mysql.Driver" />
      <set-property property="url" value="jdbc:mysql://localhost:3306/manage" />
      <set-property property="user" value="root" />
      <set-property property="password" value="root" />
      <set-property property="minCount" value="5" />
      <set-property property="maxCount" value="10" />      
      <set-property property="description" value="" />      
      <set-property property="readOnly" value="false" />
      <set-property property="autoCommit" value="false" />
      <set-property property="loginTimeout" value="" />
    </data-source>    
</data-sources>

<!-- 配置ActionForm -->
<form-beans >
    <form-bean name="loginForm" type="com.wind.struts.form.LoginForm" />
</form-beans>

<!-- 配置全局异常处理 -->
<global-exceptions>
<exception type="" key=""/>
</global-exceptions>

<!-- 配置全局跳转 -->
<global-forwards>
     <forward name="professor" path="/professor.jsp" />
</global-forwards>


<!-- 配置映射关系 -->
<action-mappings >
    <action
      attribute="loginForm"
      input="/form/login.jsp"
      name="loginForm"
      path="/login"
      scope="request"
   validate="true"
      type="com.wind.struts.action.LoginAction" >
      
      <!-- 配置局部异常处理 -->
      <exception type="" key=""/>
      
      <!-- 配置局部跳转 -->
      <forward name="student" path="/student.jsp" />
      <forward name="teacher" path="/teacher.jsp" />      
      
    </action>
</action-mappings>

<!-- 配置RequestProcessor插件 -->
<controller processorClass="com.wind.MyRequestProcessor"></controller>

<!-- 配置国际化资源文件 -->   
<message-resources parameter="com.wind.struts.ApplicationResources" />

<!-- 配置Plugin插件 -->
<plug-in className="com.wind.MyPlugin">
<!-- 配置validate验证文件 -->
    <set-property property="pathnames" value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"/>
</plug-in></struts-config>

(2)ConfigRuleSet类addRuleInstances()方法设置中对xml解析的操作(规则)

public void addRuleInstances(Digester digester) {

        digester.addObjectCreate
            ("struts-config/data-sources/data-source",
             "org.apache.struts.config.DataSourceConfig",
             "className");

      //表示遇到data-source节点的“开始”,就可以缺省创建一个org.apache.struts.config.DataSourceConfig的实例。如果xml文件设置了className的属性,就生成一个className指定类(org.apache.struts.config.DataSourceConfig2)的实例,而不是org.apache.struts.config.DataSourceConfig的实例。并将该对象压入栈顶。详细的看ObjectCreatRule类。

        digester.addSetProperties
            ("struts-config/data-sources/data-source");

   //遇到struts-config/data-sources/data-source的attributevs的“开始”,根据web-xml的根据属性列表中(attributes)的属性值对 ,<form-bean name="basMapForm" type="com.longtop.bas.web.BasMapForm"/>,则从栈顶取回该对象Object top = digester.peek();,并设置object的name=basMapForm,type="com.longtop.bas.web.BasMapForm"。该节点被解析时,SetPropertiesRule的begin()方法会被调用,并且name="basMapForm" type="com.longtop.bas.web.BasMapForm"会被封装到Attributes attributes的对象里。详细的看SetPropertiesRule类。

/***解析时相当运行的代码***/

HashMap values = new HashMap();

String name = attributes.getLocalName(0);name="name"

String value = attributes.getValue(0);value=="basMapForm"

String name = attributes.getLocalName(0);name="type"

String value = attributes.getValue(0);value=="com.longtop.bas.web.BasMapForm"

values.put(name, value);

Object top = digester.peek();//top是org.apache.struts.action.ActionFormBean的实例。

BeanUtils.populate(top, values);//是指org.apache.struts.action.ActionFormBean的实例的name和type属性值。

/***解析时相当运行的代码***/

        digester.addSetProperty
            ("struts-config/data-sources/data-source/set-property",
             "property", "value");

//遇到struts-config/data-sources/data-source/set-property的“开始”,根据web-xml的根据属性中(property)的属性值对 ,      <set-property property="user" value="root" />
      <set-property property="password" value="root" />,则从栈顶取回该对象Object top = digester.peek();,并设置object的password=root,user=root。详细的看SetPropertyRule类。


        digester.addSetNext
            ("struts-config/form-beans/form-bean",
             "addFormBeanConfig",
             "org.apache.struts.config.FormBeanConfig");

   //表示调用栈顶org.apache.struts.config.DataSourceConfig的父节点(org.apache.struts.config.ModuleConfig)相对应的对象的addDataSourceConfig的方法将该对象(DataSourceConfig,当前的栈顶对象)加入到父节点对象(ModuleConfig)。详细的看SetNextRule类。

<form-bean name="basMapForm" type="com.longtop.bas.web.BasMapForm"/>

        Object child = digester.peek(0);子节点FormBeanConfig
        Object parent = digester.peek(1);父节点org.apache.struts.config.impl.ModuleConfig

        if (paramType != null) {
            paramTypes[0] =
                    digester.getClassLoader().loadClass(paramType);//paramType是实例化rule时,已经设置成“FormBeanConfig”,加载FormBeanConfig的类。
        } else {
            paramTypes[0] = child.getClass();
        }