学习struts 的digester类解析xml文件
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();
}
Come from :http://dev.firnow.com/course/3_program/java/javajs/20100719/461328.html