Digester 是jakarta-struts分离出来的包
把xml配置文件,转换为java对象
也可以认为是java解析。
Digester可以实现动态解析根据解析规则解析xml文件
Struts中的struts-config.xml就是使用Digester做解析的。
很不错的工具
方法:
1:init()初始化 :包裹整个初始化在尝试捉住改善把意想不到的例外和错误提供更好的的反馈给开发商,会抛出ServletException
2:destroy() 优美的关闭这控制器Servlet,发布调拨在初始化的任何资源
3.doGet()
处理HTTP "Get" 请求;
请求我们处理的servlet 请求,
反应我们创造的servlet 反应,
会抛出IOException 和ServletExcepiton.
4.doPost()
处理HTTP "Post" 请求;
请求我们处理的servlet 请求,
反应我们创造的servlet 反应,
会抛出IOException 和ServletExcepiton;
5. addServletMapping(String servletName, String urlPattern)
记住servlet 映射从我们的网应用部署形容标志, 如果它是为这servlet;
servletName servlet 的名字被映射,
urlPattern 这servlet 被映射的URL 样式.
6. getInternal()
MessageResources 事例包含我们的内部消息串起的回归.
三:受保护的方法
1:initInternal() 初始化我们的内部MessageResources捆绑,如果我们无法初始化这些资源就抛出ServletException
事先定义:属性(1)
(2)在initInternal()方法中,通过调用MessageResources.getMessageResources(internalName)返回一个MessageResources类
internal.MessageResources 是org.apache.struts.util包里的; MessageResources是一个abstract 的类implements
java.io.Serializable;注意:MessageResourcesFactory是一个abstract类,每一个extends它的类都要实现abstract 的方
法 createResources (),得到一个MessageResources对象.而在此,要了解RequestUtils.applicationClass(factoryClass);
RequestUtils 同样也在org.apache.struts.util包下.在备注里简单地说明了一个这个类.
2.initOther()
首先从servlet中得到” config”和” convertNull”的值.
根据web.xml的配置"config"参数进行设定
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name> <!--即此处的设置信息-->
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>convertNull</param-name> <!--即此处的设置信息-->
<param-value>true </param-value>
</init-param>
……
</servlet>
如果从servlet中得到”config”的值不为null
就将其值付给config;
如果从servlet中得到” convertNull”的值等于("true", "yes", "on", "y", "1")注明:不区分大小写
convertNull 付值为true;
如果convertNull为真时
初始化一个ConvertUtils对象; 由于ConvertUtils.deregister()的初始化,所有的Converter都是有初始值的,所以这里Struts自己把这些初始值设置为null,即转换出错的时候返回null,而不是初始值。使用ConvertUtils类的原因是由于从form传输过来的都是String类型的值,所以我们要把它们转换成相应的类型。目前没有找到更多有关ConvertUtils的更多信息,如有了解的朋友,可以的话请帮助提供相关信息.多谢!
这里的”convertNull”是在Struts 1.0里提供的,有的文章中,在这里写的是”debug”而在目前,我所使用的web.xml文件里也只有设定
<param-name>debug </param-name> <!--即此处的设置信息-->
<param-value>2 </param-value>
</init-param>
不明白,这里有什么区别之处?
3.initServlet()
这个方法是先通过getServletConfig().getServletName(),保存servletName获取控制器servlet;再利用Digester类是的方法,
取得部署文件是的标识,解读”web.xml”部署文件,对servletMapping中的属性进行初始化.
// 默认的struts配置文件为/WEB-INF/struts-config.xml
protected String config = "/WEB-INF/struts-config.xml"; // ② initOther(); ⑤ initModuleConfig ();
// 默认的链(定义了一个按顺序执行的处理流程)配置文件
protected String chainConfig = "org/apache/struts/chain/chain-config.xml";
// ④ initChain();
protected Digester configDigester = null; // ⑤ initModuleConfig ();
// 如convertNull 为true,Java包装类(如java.lang.Integer)的初始值为null
protected boolean convertNull = false; // ② initOther();
protected MessageResources internal = null; // ① initInternal();
// 默认的 struts-core-1.3.5.jar 包 中资源文件为ActionResources.properties
protected String internalName = "org.apache.struts.action.ActionResources";
4. process(HttpServletRequest request, HttpServletResponse response)
执行标准请求处理为这个请求,并且创造对应的反应
5. initModuleConfigFactory()
根据造模块配置创建初始化的工厂.
如果getServletConfig().getInitParameter("configFactory")的值configFactory不为null
通过ModuleConfigFactory类的setFactoryClass(configFactory)初始化工厂.
6. initModuleConfig(String prefix, String paths)
ModuleConfigFactory类的createFactory(),得到一个ModuleConfigFactory对象,再用这个对象调用createModuleConfig(prefix)方法,取得ModuleConfig对象.然后通过initConfigDigester()方法来生成一个digester(得到解析XML的digester).
// Process each specified resource path
while (paths.length() > 0) {
//开始解析指定路径文件名的文件
digester.push(config);
String path = null;
//文件是否为多个并且使用","分割
int comma = paths.indexOf(',');
//存在多个配置文件
if (comma >= 0) {
//先解析第一个
path = paths.substring(0, comma).trim();
//paths存放剩余的文件名下次再处理
paths = paths.substring(comma + 1);
} else {
//当前只存在一个
path = paths.trim();
//没有剩余,下次循环根据上面一句将会在唯一的循环退出点"point break"
//退出循环
paths = "";
}
//全部解析完成跳出循环
//point break
if (path.length() < 1) {
break;
}
然后对每一块调用.parseModuleConfigFile(digester, path);方法解析。注意,这个方法实际上只path为我们要解析的xml文件,digester用来初始化完成后保存到digester中(这里是struts1.2中修改的方法)。
调用getServletContext().setAttribute(Globals.MODULE_KEY + config.getPrefix(),config);将初使化完成后的config保存到servletContext中.
7.initModuleMessageResources(ModuleConfig config)
通过存储在ModuleConfig中的MessageResourcesConfig对象,逐个初始化MessageResource,然后再把初始化好的MessageResources存放到ServletContext中,attributeName为MessageResourcesConfig.getKey() + ModuleConfig.getPrefix()。
8.initModuleDataSources(ModuleConfig config)
通过存储在ModuleConfig中的DataSourceConfig对象,逐个初始化DataSource。然后对于每一个DateSource通过BeanUtils.populate(ds, dscs[i].getProperties())方法初始化其属性。再把初始化好的DateSource存放到ServletContext中,attributeName为DataSourceConfig.getKey() + ModuleConfig.getPrefix()。同时也存放到名位dataSources的FastHashMap中,key为DataSourceConfig.getKey()。
这里还会根据生成的DateSource对象是否是GenericDataSource类型,如果是则调用
GenericDataSource.open()方法。GenericDataSource是一个非常简单的数据库连接池,它的
open()方法用来初始化连接池,生成最小数目的GenericConnection,这里的open()方法根据
String driver变量是否为null来判断是否已经被初始化过。需要仔细说明的是getConnection()
方法,它首先从连接池中取出GenericConnection对象,然后检查其是否是可链接的,如果是就
返回,否则继续取出,同时activeCount-1。如果没有取到,则会检查当前可使用的
GenericConnection是否达到最大值(activeCount < maxCount),如果没有,调用
createConnection()方法声成一个新的GenericConnection,然后检查其是否是可链接,如果可以
则返回。returnConnection(GenericConnection conn)方法则是通过把GenericConnection放回到
连接池,然后activeCount-1。
这个方法中使用到了ServletContextWriter类,DateSource的log信息就通过这个类写入。对这个
类说明如下:
它继承自PrintWriter,而PrintWriter又继承自Writer。Writer类所作的事情就是在同步的情况下
调用abstract方法:abstract public void write(char cbuf[], int off, int len),这个方法
将会根据调用者的需要由调用者实现。
PrintWriter则首先通过ensureOpen()方法检验这个类中是否有写入的对象(Writer类或其子类),
如果有则根据不同的情况调用这个写入对象的write方法(out.write(....))。这个类的print(...)
方法就是据不同的情况调用相应的write(...)方法。而println(...)与之的区别就是每次多写入一
个换行字符串。还有一个区别是println(...)会根据是否需要autoflush进行flush,而write(...)
方法不会。
ServletContextWriter类的作用是把字符写入ServletContext中。ServletContextWriter类方法中
真正实现了write方法:
public void write(char c) {
if (c == '\n')
flush();
else if (c != '\r')
buffer.append(c);
}
public void flush() {
if (buffer.length() > 0) {
context.log(buffer.toString());
buffer.setLength(0);
}
}
9.initModulePlugIns(moduleConfig)
通过存储在ModuleConfig中的PlugInConfig对象,逐个初始化PlugIn对象,存放到一个数组中,
然后再把这个数组存放到ServletContext中,attributeName为
Globals.PLUG_INS_KEY + ModuleConfig.getPrefix()。
对每一个生成的PlugIn对象通过
BeanUtils.populate(plugIns[i], plugInConfigs[i].getProperties())方法初始化其属性。然后
再把PlugInConfig对象存放到由其生成的PlugIn对象中。
最后,通过plugIns[i].init(this, (ModuleConfig) config)初始化这个plugIn对象。
完成了struts这个初始化以后,会调用ModuleConfig.freeze()令这个ModuleConfig变得不可改变。然后
会遍历ServletConfig中的initParameterNames,如果有以"config/"开头的,则通过这个parameter
的值继续初始化其它的ModuleConfig,且这个ModuleConfig的prefix为"config/"后的字符串。
同样调用如下方法:
initModuleMessageResources(moduleConfig);
initModuleDataSources(moduleConfig);
initModulePlugIns(moduleConfig);
moduleConfig.freeze();
最后调用destroyConfigDigester()释放内存。
第一篇 struts的初始化
struts 的核心类是org.apache.struts.action.ActionServlet,这个类将会在struts第一次使用时,
作为servlet初始化并存入tomcat容器。很显然的,初始化将会调用init方法初始化相应的数据。
一、initInternal()方法:
通过调用MessageResources.getMessageResources(internalName)方法生成一个
MessageResources类,getMessageResources是通过调用MessageResourcesFactory.
createResources(config)来实现的。至于MessageResourcesFactory是一个abstract类,任何
继承自它的类都要实现createResources方法,生成MessageResources对象。整个程序生成
MessageResourcesFactory使用了如下技巧:
MessageResourcesFactory.factoryClass = factoryClass;
MessageResourcesFactory.clazz = null;
首先会通过factoryClass来定义一个类全名,然后通过ClassLoader.loadClass
(factoryClass)方法来生成这个类,并赋给clazz,然后通过newInstance来生成一个对象。
在本程序中,生成MessageResources对象实际就是对如下属性进行了初始化:
this.factory = factory;("org.apache.struts.util.PropertyMessageResourcesFactory")
this.config = config;("org.apache.struts.action.ActionResources")
this.returnNull = returnNull;(true/false)
对于MessageResources类的作用是根据不同的Locate来格式化相应的string。或者把你需要改变
的string存放到数组中,然后通过getMessage(Locale locale, String key, Object args[])
方法来格式化。然后把格式好的string存放到HashMap里,这样就可以为以后重用。这里的key是
使用的locale.toString() + "." + key
在PropertyMessageResources中的loadLocale方法用来读取resource的初始化信息。首先它会
通过一个HashMap检测这个localKey相关的message是否已经被初始化了,如果被初始化过就跳
出,检测的方法是locales.get(localeKey) != null。
然后会读取如下一个文件:
org/apache/struts/action/ActionResources_(localKey).properties,然后进行如下操作:
Properties props = new Properties();
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
is = classLoader.getResourceAsStream(name);
props.load(is);
Iterator names = props.keySet().iterator();
while (names.hasNext()) {
String key = (String) names.next();
if (log.isTraceEnabled()) {
log.trace(" Saving message key '" + messageKey(localeKey, key));
}
messages.put(messageKey(localeKey, key), props.getProperty(key));
}
PropertyMessageResources 就是通过上面的loadLocale方法查找与Locale locale, String key
相对对应的Message.查找的次序如下locale.toString(),然后是
localeKey = localeKey.substring(0, underscore),然后是defaultLocale,然后是key。
最后,resource类的结构如下:
PropertyMessageResources extends MessageResources
PropertyMessageResourcesFactory extends MessageResourcesFactory
二、initOther()方法:
从servlet中获取config和debug两个参数,然后初始化ConvertUtils对象。由于
ConvertUtils.deregister()的初始化,所有的Converter都是有初始值的,所以这里Struts自己
把这些初始值设置为null,即转换出错的时候返回null,而不是初始值。使用ConvertUtils类的
原因是由于从form传输过来的都是String类型的值,所以我们要把它们转换成相应的类型。
提到几个技巧:
*public boolean isIndexed() {
if (type == null) {
return (false);
//技巧一:判断是否是一个Array类的方法
} else if (type.isArray()) {
return (true);
//技巧二:判断type是否是List的一个父类或者父接口,或者与List为同一个类
//要注意如果List是另一个primitive的TYPE类,那么type必须也是这个类才会
//返回true,否则都是false。注意long.TYPE与Long.class是不同的
} else if (List.class.isAssignableFrom(type)) {
return (true);
} else {
return (false);
}
}
*//componentType为Array类所存储的元素的类别
Class componentType = indexedProperty.getClass().getComponentType();
//生成一个新的Array
Object newArray = Array.newInstance(componentType, (index + 1));
System.arraycopy(indexedProperty, 0, newArray, 0, length);
indexedProperty = newArray;
set(name, indexedProperty);
int newLength = Array.getLength(indexedProperty);
for (int i = length; i < newLength; i++) {
Array.set(indexedProperty, i, createProperty(name+"["+i+"]", componentType));
}
三、initServlet()方法:
这个方法主要是通过digester类解析web.xml,对String servletMapping属性进行初始化。对于
digester说明如下:这是一个基于DOM的SAX实现的类,它是事件触发的,根据xml文件的结构,
每次读到一个节点元素就会触发一个事件。
InputStream input = getServletContext().getResourceAsStream("/WEB-INF/web.xml");
这是一个比较少见的方法。首先通过this.servletName = getServletConfig().
getServletName()获取servlet的名称,然后根据
if (servletName.equals(this.servletName)) {
this.servletMapping = urlPattern;
}
来判断当前读到的servlet名称是否是我们运行的servlet的名称,如果是,就把url-pattern作为
我们的servletMapping。
四、getServletContext().setAttribute(Globals.ACTION_SERVLET_KEY, this)
把自己存储到servletContext中,属性名为Globals.ACTION_SERVLET_KEY。
五、ModuleConfig moduleConfig = initModuleConfig("", config)
这个方法使用由initOther()方法获取的config值为要解析的xml路径,用来初始化ModuleConfig。
它首先采用与生成MessageResourcesFactory同样的方法产生一个MessageResourcesFactory对象:
MessageResourcesFactory为一个抽象类,每一个继承它的类都要实现
createModuleConfig(String prefix)方法。本程序使用的缺省的MessageResourcesFactory类为
org.apache.struts.config.impl.DefaultModuleConfigFactory,它
的createModuleConfig(String prefix)方法会生成一个ModuleConfigImpl类。
ModuleConfigImpl类相当于一个JavaBean,用来存放一个web模块运行时所需要的配置信息。当
然,一个web模块可以拥有多个ModuleConfig,但是缺省的是prefix长度为0的ModuleConifg。它
的每个属性几乎都是由HashMap组成的,它通过一个configured布尔值来描述当前的ModuleConfig
是否已经被初始化完毕,在每存放一个属性的时候都会监测这个值。如果初始化完毕而还要改变
里面的属性值,则会报出IllegalStateException("Configuration is frozen")异常,现在对它
的属性简单说明如下:
* protected HashMap actionConfigs:
这个HashMap用来存储ActionConfig对象。
* protected HashMap dataSources
这个HashMap用来存储DataSourceConfig对象。
* protected HashMap exceptions
这个HashMap用来存储ExceptionConfig对象。
* protected HashMap formBeans
这个HashMap用来存储FormBeanConfig对象。
* protected HashMap forwards
这个HashMap用来存储ForwardConfig对象。
* protected HashMap messageResources
这个HashMap用来存储MessageResourcesConfig对象。
* protected ArrayList plugIns
这个HashMap用来存储PlugInConfig对象。
* protected ControllerConfig controllerConfig
ControllerConfig类
* protected boolean configured
标志这个ModuleConfig是(true)否(false)配置完成。
* protected String prefix
用来标志和区分ModuleConfig类,同时在使用上面的config类初始化相应的资源以后,也是通
过这个prefix来区分所属的不同的web模块。
* protected String actionMappingClass = "org.apache.struts.action.ActionMapping"
ActionMapping类名,缺省为org.apache.struts.action.ActionMapping。
初始化ModuleConfig的方法如下:
首先是使用getServletConfig().getInitParameter("mapping")来获取设定的ActionMapping类
名,然后通过initConfigDigester()方法来生成一个digester。最后用","分隔config,对每一
块调用parseModuleConfigFile(prefix, paths, config, digester, path)方法解析。注意,这
个方法实际上只有两个参数是有意义的:path为我们要解析的xml文件,config用来初始化完成
后保存到servletContext中。
如果ModuleConfig中存放的FormBeanConfig为Dydamic类型,那么就调用
DynaActionFormClass.createDynaActionFormClass(FormBeanConfig)初始化
DynaActionFormClass,并存放到DynaActionFormClass.dynaClasses 的 static HashMap中。这
里的key为FormBeanConfig.getName() + moduleConfig.getPrefix()。
如果当前的ModuleConfig为缺省的ModuleConfig,那么将会调用如下几个方法:
defaultControllerConfig(config)
defaultMessageResourcesConfig(config)
defaultFormBeansConfig(config)
defaultForwardsConfig(config)
defaultMappingsConfig(config)
在struts1.1以后,这个特例将会被废弃:
defaultControllerConfig(config)为ControllerConfig通过getInitParameter(s)方法初始化如
下几个属性:bufferSize,content,locale(true/false),maxFileSize,nocache(true/false)
,multipartClass,tempDir。
defaultMessageResourcesConfig(config)为MessageResourcesConfig通过getInitParameter(s)
方法初始化如下几个属性:application,factory,null(true/false)。
其它的几个方法就是获取不同的对象,然后把它们相应的存储到servlet中。关心如下:
ActionFormBeans=>FormBeanConfig,ActionForwards=>ForwardConfig,
ActionMappings=>ActionConfig。
六、initModuleMessageResources(ModuleConfig config)
通过存储在ModuleConfig中的MessageResourcesConfig对象,逐个初始化MessageResource,
然后再把初始化好的MessageResources存放到ServletContext中,attributeName为
MessageResourcesConfig.getKey() + ModuleConfig.getPrefix()。
七、initModuleDataSources(ModuleConfig config)
通过存储在ModuleConfig中的DataSourceConfig对象,逐个初始化DataSource。然后对于每一个
DateSource通过BeanUtils.populate(ds, dscs[i].getProperties())方法初始化其属性。再把初
始化好的DateSource存放到ServletContext中,attributeName为
DataSourceConfig.getKey() + ModuleConfig.getPrefix()。同时也存放到名位dataSources的
FastHashMap中,key为DataSourceConfig.getKey()。
这里还会根据生成的DateSource对象是否是GenericDataSource类型,如果是则调用
GenericDataSource.open()方法。GenericDataSource是一个非常简单的数据库连接池,它的
open()方法用来初始化连接池,生成最小数目的GenericConnection,这里的open()方法根据
String driver变量是否为null来判断是否已经被初始化过。需要仔细说明的是getConnection()
方法,它首先从连接池中取出GenericConnection对象,然后检查其是否是可链接的,如果是就
返回,否则继续取出,同时activeCount-1。如果没有取到,则会检查当前可使用的
GenericConnection是否达到最大值(activeCount < maxCount),如果没有,调用
createConnection()方法声成一个新的GenericConnection,然后检查其是否是可链接,如果可以
则返回。returnConnection(GenericConnection conn)方法则是通过把GenericConnection放回到
连接池,然后activeCount-1。
这个方法中使用到了ServletContextWriter类,DateSource的log信息就通过这个类写入。对这个
类说明如下:
它继承自PrintWriter,而PrintWriter又继承自Writer。Writer类所作的事情就是在同步的情况下
调用abstract方法:abstract public void write(char cbuf[], int off, int len),这个方法
将会根据调用者的需要由调用者实现。
PrintWriter则首先通过ensureOpen()方法检验这个类中是否有写入的对象(Writer类或其子类),
如果有则根据不同的情况调用这个写入对象的write方法(out.write(....))。这个类的print(...)
方法就是据不同的情况调用相应的write(...)方法。而println(...)与之的区别就是每次多写入一
个换行字符串。还有一个区别是println(...)会根据是否需要autoflush进行flush,而write(...)
方法不会。
ServletContextWriter类的作用是把字符写入ServletContext中。ServletContextWriter类方法中
真正实现了write方法:
public void write(char c) {
if (c == '/n')
flush();
else if (c != '/r')
buffer.append(c);
}
public void flush() {
if (buffer.length() > 0) {
context.log(buffer.toString());
buffer.setLength(0);
}
}
八、initModulePlugIns(moduleConfig)
通过存储在ModuleConfig中的PlugInConfig对象,逐个初始化PlugIn对象,存放到一个数组中,
然后再把这个数组存放到ServletContext中,attributeName为
Globals.PLUG_INS_KEY + ModuleConfig.getPrefix()。
对每一个生成的PlugIn对象通过
BeanUtils.populate(plugIns[i], plugInConfigs[i].getProperties())方法初始化其属性。然后
再把PlugInConfig对象存放到由其生成的PlugIn对象中。
最后,通过plugIns[i].init(this, (ModuleConfig) config)初始化这个plugIn对象。
九、初始化结束
完成了这个初始化以后,会调用ModuleConfig.freeze()令这个ModuleConfig变得不可改变。然后
会遍历ServletConfig中的initParameterNames,如果有以"config/"开头的,则通过这个parameter
的值继续初始化其它的ModuleConfig,且这个ModuleConfig的prefix为"config/"后的字符串。
同样调用如下方法:
initModuleMessageResources(moduleConfig);
initModuleDataSources(moduleConfig);
initModulePlugIns(moduleConfig);
moduleConfig.freeze();
最后调用destroyConfigDigester()释放内存。