Struts源码分析笔记(struts初始化)
Struts源码分析笔记(struts初始化)
看了很多的例子,大家都认为,要很好的了解Struts是要认真的分析他的源代码;同样在网络上也有很多关于源码分析的文章;为了我能自觉地坚持下来,本人还是决定,将每天学习的成果写出来,作为一个笔记;这样对我个人来讲比较有成就感,如果不有对之处,还能请高人给予指点.
首先就是actionServlet
打开actionServlet类,就会看到
protected FastHashMap dataSources = new FastHashMap();
它来自org.apache.commons.collections.FastHashMap;
org.apache.commons这是个著名的包,如果有时间建议你花上一段时间去研究使用它。
一 属性
FastHashMap 相对于 HashMap来讲它是一个快速的HashMap;
(1) protected String internalName ="org.apache.struts.action.ActionResources"; protected MessageResources internal = null; (2) protected String config = "/WEB-INF/struts-config.xml"; |
(1)公共的方法:
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 事例包含我们的内部消息串起的回归.
(2)受保护的方法
1.initInternal()
初始化我们的内部MessageResources 捆绑,
如果我们无法初始化这些资源就抛出ServletException .
事先定义:属性(1)(2)
在initInternal()方法中,通过调用MessageResources.getMessageResources(internalName)返回一个MessageResources类 internal .
MessageResources 是org.apache.struts.util包里的; MessageResources是一个abstract 的类implements java.io.Serializable;
/** * Create and return an instance of <code>MessageResources</code> for the * created by the default <code>MessageResourcesFactory</code>. * * @param config Configuration parameter for this message bundle. */ public synchronized static MessageResources getMessageResources(String config) { if (defaultFactory == null) { defaultFactory = MessageResourcesFactory.createFactory(); } return defaultFactory.createResources(config); } |
config = "/WEB-INF/struts-config.xml"
MessageResourcesFactory 中的createResources(config);通过"/WEB-INF/struts-config.xml"文件中设定的参数构造出MessageResources对象.
MessageResourcesFactory 是org.apache.struts.util包里的MessageResourcesFactory;是一个abstract 的类implements java.io.Serializable;
/** * Create and return a <code>MessageResourcesFactory</code> instance of the * appropriate class, which can be used to create customized * <code>MessageResources</code> instances. If no such factory can be * created, return <code>null</code> instead. */ public static MessageResourcesFactory createFactory() { // Construct a new instance of the specified factory class try { if (clazz == null) clazz = RequestUtils.applicationClass(factoryClass); MessageResourcesFactory factory = (MessageResourcesFactory) clazz.newInstance(); return (factory); } catch (Throwable t) { LOG.error("MessageResourcesFactory.createFactory", t); return (null); } } } |
注意: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中的属性进行初始化.
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()释放内存。