Struts 源码最新版本为 struts-1.3.8-src.zip ( 12-Mar-2007 00:06 )
学习笔记使用struts-1.3.5-src.zip 的源码,
下载地址:http://archive.apache.org/dist/struts/source/
1. 在web.xml中通过下面定义把所有的*.do交给ActionServlet处理
<!-- Standard Action Servlet Configuration (with debugging) -->
<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,
/WEB-INF/struts-config-Wildcard.xml
</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<!-- Standard Action Servlet Mapping -->
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
2. 下面研究一下struts的源码,由于servlet设置了load-on-startup,所以tomcat启动时会加载ActionServlet,也就是会执行ActionServlet中的init()方法,Struts 的初始化实现就是在这里实现的。
注: 由于servlet的生命周期为 web容器加载和实例化类/init()初始化/service()请求处理/destroy()四个阶段,而init()方法在tomcat启动后只执行一次,所以如果想在tomcat启动后用debug模式查看ActionServlet中init()方法的执行,可以把上面的<load-on-startup>2</load-on-startup>注释掉就可以了(不过真正开发时还是需要的)。
3. 在ActionServlet中定义了一些常量,如下:
// 默认的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";
// ① initInternal();
// 一些文档类型定义,用来验证相应的配置文件如struts-config.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",
"-//Apache Software Foundation//DTD Struts Configuration 1.2//EN",
"/org/apache/struts/resources/struts-config_1_2.dtd",
"-//Apache Software Foundation//DTD Struts Configuration 1.3//EN",
"/org/apache/struts/resources/struts-config_1_3.dtd",
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN",
"/org/apache/struts/resources/web-app_2_3.dtd"
}; // ③ initServlet();
protected String servletMapping = null; // ③ initServlet();
protected String servletName = null; // ③ initServlet();
4. ActionServlet 中的init()方法执行流程如下
① 内部资源文件 ActionResources.properties 的初始化 initInternal();
protected MessageResources internal = null; // ① initInternal();
protected String internalName = "org.apache.struts.action.ActionResources"; // ① initInternal();
// initInternal 方法中通过下面得到一个MessageResources对象
internal = MessageResources.getMessageResources(internalName);
此资源文件主要包括一些消息信息的定义,具体可参考org.apache.struts.action下的ActionResources.properties文件
在MessageResources.java中的getMessageResources方法,
if (defaultFactory == null) {
defaultFactory = MessageResourcesFactory.createFactory(); // ⑴
}
return defaultFactory.createResources(config); // 传入internalName // ⑵
⑴
MessageResourcesFactory.createFactory() 所做的工作:
protected static transient Class clazz = null;
protected static String factoryClass =
"org.apache.struts.util.PropertyMessageResourcesFactory";
clazz = RequestUtils.applicationClass(factoryClass);
而RequestUtils.applicationClass通过classLoader加载一个
org.apache.struts.util.PropertyMessageResourcesFactory
⑵
defaultFactory.createResources(config) 所做的工作:
this.factory = factory;
("org.apache.struts.util.PropertyMessageResourcesFactory")
this.config = config;("org.apache.struts.action.ActionResources")
this.returnNull = returnNull;(true)
PropertyMessageResourcesFactory extends MessageResourcesFactory
返回一个MessageResources对象
② 调用 initOther(); 从web.xml中加载ActionServlet的初始化参数,包括config/ convertNull
protected String config = "/WEB-INF/struts-config.xml"; // ② initOther();
protected boolean convertNull = false; // ② initOther();
// 得到web.xml中"config"参数
String value;
value = getServletConfig().getInitParameter("config");
if (value != null) {
config = value;
}
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name> <!-- 得到"config"参数-->
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>convertNull</param-name> <!-- 得到"convertNull"参数-->
<param-value>true</param-value>
</init-param>
.......
</servlet>
// 获得convertNull的值(true/yes/on/y/1)
getServletConfig().getInitParameter("convertNull");
如果这个参数的值为 true (true/yes/on/y/1) , 数值型(BigDecimal/BigInteger/Boolean/Byte/Character/Double/Float/Integer/Long/Short)的Java 包装类(比如java.lang.Integer)的初始值为null,而非0。缺省值[false]
使其初始值为null的方法如下:
// 将所有的转换器注销掉
ConvertUtils.deregister();
// 为指定类型clazz注册转换器converter
ConvertUtils.register(new BigDecimalConverter(null), BigDecimal.class);
ConvertUtils.register(new BigIntegerConverter(null),BigInteger.class);
.......
注: ConvertUtils 用法如下
deregister () 和 deregister (java.lang.Class clazz)
注销转换器,前者将所有的转换器注销掉,后者只注销对应于clazz的转换器register( Converter converter, java.lang.Class clazz)
为指定类型clazz注册转换器converter。如果clazz已经存在一个对应的转换器,那么converter覆盖原来的转换器。
③ 调用 initServlet(); 从web.xml中加载ActionServlet的初始化参数如servlet-name,加载DTD文件并把其放入HashMap缓存,读取并解析web.xml的内容
// Remember our servlet name
getServletConfig().getServletName();
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name> <!-- 得到"config"参数-->
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>convertNull</param-name> <!-- 得到"convertNull"参数-->
<param-value>true</param-value>
</init-param>
.......
</servlet>
// Prepare a Digester to scan the web application deployment descriptor
Digester digester = new Digester();
// 把当前的 ActionServlet 对象放入到解析堆栈中
digester.push(this);
// 指明要考虑命名空间
digester.setNamespaceAware(true);
// 缺省值[false] ,解析器只是检查XML是否格式良好(well formed)
digester.setValidating(false);
// Register our local copy of the DTDs that we can find
// struts 可使用 struts-core-1.3.5.jar 包 中的DTD中来处理struts配置文件,这样可适用于那些没有连接到internet的应用环境
for (int i = 0; i < registrations.length; i += 2) {
URL url = this.getClass().getResource(registrations[i + 1]);
if (url != null) {
// 读取DTD文件并把其放入 HashMap 缓存
digester.register(registrations[i], url.toString());
}
}
/************************************************************
// 一些文档类型定义,用来验证相应的配置文件如struts-config.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",
"-//Apache Software Foundation//DTD Struts Configuration 1.2//EN",
"/org/apache/struts/resources/struts-config_1_2.dtd",
"-//Apache Software Foundation//DTD Struts Configuration 1.3//EN",
"/org/apache/struts/resources/struts-config_1_3.dtd",
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN",
"/org/apache/struts/resources/web-app_2_3.dtd"
}; // ③ initServlet();
************************************************************/
// Configure the processing rules that we need
// 运行时,digester 就会调用 ActionServlet中的 addServletMapping() 方法,并传入两个参数
digester.addCallMethod("web-app/servlet-mapping", "addServletMapping", 2);
digester.addCallParam("web-app/servlet-mapping/servlet-name", 0);
digester.addCallParam("web-app/servlet-mapping/url-pattern", 1);
得到
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
/************************************************************
// 来判断当前 servlet 名称是否为正在运行的 servlet 名称,如是,就把 url-pattern 作为 servletMapping
public void addServletMapping(String servletName, String urlPattern) {
if (servletName == null) {
return;
}
if (servletName.equals(this.servletName)) {
if (log.isDebugEnabled()) {
log.debug("Process servletName=" + servletName
+ ", urlPattern=" + urlPattern);
}
this.servletMapping = urlPattern;
}
}
************************************************************/
// 读取配置文件web.xml的内容
InputStream input = getServletContext().getResourceAsStream("/WEB-INF/web.xml");
// 如找不到/WEB-INF/web.xml文件,则报错
if (input == null) {
log.error(internal.getMessage("configWebXml"));
throw new ServletException(internal.getMessage("configWebXml"));
}
/************************************************************
// 报错信息定义在org/apache/struts/action/ActionResources.properties中
configWebXml=The /WEB-INF/web.xml was not found.
************************************************************/
// 解析input流文件,每读到一个节点元素就触发一个事件
digester.parse(input);
注: Digester 是一个基于 DOM 的 SAX 实现的类,它是事件触发的,可以将XML文件转换为任意的Java对象,支持规则的对任意XML文档的处理。原先是struts项目的一部分,后因其通用性而划归Commons子项目。
// 把servletMapping存储到servletContext中,属性名为Globals.SERVLET_KEY ( " org.apache.struts.action.SERVLET_MAPPING " )
if (servletMapping != null) {
getServletContext().setAttribute(Globals.SERVLET_KEY,servletMapping);
}
-----------------------------------------------------
java.lang.object
|
+--javax.servlet.genericservlet
|
+--javax.servlet.http.httpservlet
|
+--org.apache.struts.action.actionservlet
struts提供了一个缺省版本的actionservlet类,你可以继承这个类,覆盖其中的一些方法来达到你的特殊处理的需要。actionservlet继承与javax.servlet.http.httpservlet,所以在本质上它和一个普通的servlet没有区别,你完全可以把它当做一个servlet来看待,只是在其中完成的功能不同罢了。actionservlet主要完成如下功能:
将一个来自客户端的uri映射到一个相应的action类
如果是这个action类是第一次被调用,那么实例化一个并放入缓存
如果在配置文件(struts-config.xml)中指定了相应的actionform,那么从request中抓取数据填充formbean
调用这个action类的perform()方法,传入actionmapping的一个引用,对应的actionform、以及由容器传给actionservlet的httpservletrequest、httpservletresponse对象。
确省版本的actionservlet会从配置文件web.xml中读取如下初始化参数:
application
应用使用的资源包(resources bundle)的基类
factory
用于创建应用的messageresources对象的messageresourcesfactory的类名。确省是org.apache.struts.util.propertymessageresourcesfactory。
config
struts的配置文件,确省是/web-inf/struts-config.xml。注意这儿是与应用context关联的相对路径。
content
定义了确省的内容类型和编码格式,它会被自动地被设置到每个response中,如果jsp/servlet中没有明确的设置。确省是text/html。
debug
调试信息的级别。默认为0,比当前级别高的调试信息会被log到日志文件中。
detail
与debug的作用类似,只是这个detail是initmapping()时专用的。调试信息会被打印到system.out,而不是日志文件。
formbean
actionformbean的实现类,确省为org.apache.struts.action.actionformbean
forward
应用中使用的actionforward类,确省是org.apache.struts.action.actionforward。
locale
指定了确省使用的locale对象。设为true,当得到一个session时,会自动在session中存储一个以action.locale_key标示的locale对象,如果session中还没有与action.locale_key绑定的locale对象。
mapping
应用中使用的actionmapping类,确省是org.apache.struts.action.actionmapping。
multipartclass
文件上传使用的mutipartrequesthandler的实现类。确省为org.apache.struts.upload.diskmultipartrequesthandler
nocache
如果设为true,那么actionservlet会自动在每个到客户端的响应中添加nocache的html头,这样客户端就不会对应用中的页面进行缓存。确省为false
null
如果设置为true,那么应用在得到一个未定义的message资源时,会返回null,而不是返回一个错误信息。确省是true。
maxfilesize
文件上传的大小上限,确省为250m
buffersize
文件上传时的缓冲区的大小,确省为4m
tempdir
设置用于上传时的临时目录。工作目录会作为一个servlet环境(context)的属性提供。
validate
are we using the new configuration file format?确省为true。
validating
在解析配置xml文件是是否进行有效性的验证。确省为true
actionservlet中应用了命令设计模式。
一个servlet在由容器生成时,首先会调用init()方法进行初始化,在接到一个http请求时,调用相应的方法进行处理;比如get请求调用doget()方法,post请求调用dopost()方法。所以首先看看actionservlet的init()方法,你就会很清楚为什么actionservlet可以完成这些功能了。
init()
在它的init()方法中,actionservlet依次调用如下protected的方法完成初始化:
initactions() - 大家可能还曾有这个疑问:struts为什么可以找到一个请求uri对应的action类呢?答案就在这儿,actionservlet有一个actions属性,类型为org.apache.struts.util.fasthashmap,用于存储以类的全名为key的已实例化的action类。在init()时首先调用的就是initactions()方法,在这个方法中只是简单的清除map中的所有的名值对,
synchronized (actions) {
actions.setfast(false);
actions.clear();
actions.setfast(true);
}
首先把actions设为slow模式,这时对fasthashmap的访问是线程同步的,然后清除actions中的所有的已存在的名/值对,最后再把actions的模式设为fast。由于fasthashmap是struts在java.util.hashmap的基础上的一个扩展类,是为了适应多线程、并且对hashmap的访问大部分是只读的特殊环境的需要。大家知道java.util.hashmap是非线程安全的,所以hashmap一般适用于单线程环境下。org.apache.struts.fasthashmap就是继承于java.util.hashmap,在其中添加多线程的支持产生的。在fast模式下的工作方式是这样的:读取是非线程同步的;写入时首先克隆当前map,然后在这个克隆上做写入操做,完成后用这个修改后的克隆版本替换原来的map。那么在什么时候会把actions类添加到这个map中呢?我们已经提到了struts是动态的生成action类的实例的,在每次actionservlet接收到一个get或post的http请求时,会在这个map中查找对应的action类的实例,如果不存在,那么就实例化一个,并放入map中。可见这个actions属性起到了对action类实例的缓存的作用。
initinternal() - 初始化actionservlet内部使用的资源包messageresources,使用messageresources.getmessageresources(internalname)得到 internalname为"org.apache.struts.action.actionresources"对应的actionresources.properties文件。这个资源包主要用于actionservlet处理过程中的用到的提示信息,这儿不展开讨论。
initdebug() - 从web.xml中读取本应用的debug级别参数getservletconfig().getinitparameter("debug"),然后赋给debug属性。
initapplication()- 初始化应用资源包,并放置入servletcontext中。
string factory =getservletconfig().getinitparameter(“factory”);
string oldfacory = messageresourcesfactory.getfactoryclass();
if (factory !=null)
messageresourcesfactory.setfactoryclass(factory);
string value = getservletconfig().getinitparameter("application");
messageresourcesfactory factoryobject =
messageresourcesfactory.createfactory();
application = factoryobject.createresources(value);
messageresourcesfactory.setfactory(oldfactory);
getservletcontext().setattribute(action.messages_key, application);
说明:文中引用的代码片断可能会省略了一些例外检查等非主线的内容,敬请注意。
首先从配置文件中读取factory参数,如果这个参数不为空,那么就在messageresourcesfactory中使用这个指定的factory类;否则,使用默认的工厂类org.apche.struts.util.propertymessageresourcefactory。然后调用messageresourcesfactory的静态createfactory()方法,生成一个具体的messageresourcefactory对象(注意:messageresourcesfactory是抽象类)。这样就可以调用这个具体的messageresourcefactory的createresource()方法得到配置文件(web.xml)中定义的资源文件了。
上面的application对象类型为messageresources。在web.xml中在配置actionservlet时可以指定一个特定的工厂类。不能直接messageresourcesfactory的createresources()方法,因为这个方法是abstract的。创建factoryobject的过程如下:
messageresourcefactory factoryobject=
messageresourcesfactory.createfactory();
application = factoryobject.createresources(value);
<li>initmapping() - 为应用初始化mapping信息actionservlet有一个protected的属性:mapping,封装了一个actionmapping的对象集合,以便于管理、查找actionmapping。mappings是org.apache.struts.action.actionmappings类的实例。主要有两个方法:addmapping(actionmapping mapping)和findmapping(string path)。actionmapping也是使用上面提到的org.apache.struts.util.fasthashmap类来存储所有的actionmapping对象。
mappings.setservlet(this);
……
// initialize the name of our actionformbean implementation class
value = getservletconfig().getinitparameter("formbean");
if (value != null)
formbeanclass = value;
// initialize the name of our actionforward implementation class
value = getservletconfig().getinitparameter("forward");
if (value != null)
forwardclass = value;
// initialize the name of our actionmapping implementation class
value = getservletconfig().getinitparameter("mapping");
if (value != null)
mappingclass = value;
在initmapping()中,首先链接mappings对象到本servlet实例。其实这句话的作用很简单,在actionmappings中会有一个actionservlet类型的属性,这个属性就界定了这个actionmappings对象所属的actionservlet。struts的实现比较灵活,其中的actionformbean、actionforward、actionmapping类你完全可以使用自己实现的子类,来定制struts的工作方式。上面的代码就从配置文件(web.xml)中读取formbean、forward、mapping参数,这些参数就是你定制的actionformbean、actionforward、actionmapping类名。
// initialize the context-relative path to our configuration resources
value = getservletconfig().getinitparameter("config");
if (value != null)
config = value;
// acquire an input stream to our configuration resource
inputstream input = getservletcontext().getresourceasstream(config);
digester digester = null;
digester = initdigester(detail);
try {
formbeans.setfast(false);
forwards.setfast(false);
mappings.setfast(false);
digester.parse(input);
mappings.setfast(true);
forwards.setfast(true);
formbeans.setfast(true);
} catch (saxexception e) {
throw new servletexception
(internal.getmessage("configparse", config), e);
} finally {
input.close();
}
从web.xml读取struts的配置文件的位置。使用org.apache.struts.digester.digester解析config参数标示的配置文件,通常为“/web-inf/struts-config.xml”,解析出所有的data-source、form-bean、action-mapping、forward。从上面的程序片断看到,digester仅仅调用了一个parse()方法,那么,digester是怎样把解析struts-config.xml文件并把解析的结果form-bean等信息存储到属性变量formbeans等中的呢?你可以注意到在调用digester.parse(inputstream)之前,首先调用了initdigester()方法:
digester digester = new digester();
digester.push(this);
digester.addobjectcreate("struts-config/action-mappings/action",
mappingclass, "classname");
digester.addsetproperties("struts-config/action-mappings/action");
digester.addsetnext("struts-config/action-mappings/action",
"addmapping",
"org.apache.struts.action.actionmapping");
digester.addsetproperty
("struts-config/action-mappings/action/set-property",
"property", "value");
在这个方法中首先生成一个digester对象,然后设置解析的规则和回调,如果你对xml、sax不是很熟,这儿不必纠缠太深。要注意的是addsetnext()方法,设置了每一个要解析元素的set next回调方法,而这个方法就是由digester解析器的父提供的。上面的片断中的“addmapping”就是actionservlet本身定义的一个方法,将由digester回调。digester就是籍此把解析出的每一个formbean、actionforward、actionmapping等存储到属性变量formbeans、forwards、mappings等中的。
initupload() - 初始化有关upload的一些参数,比如:buffersize、tempdir。
initdatasource() -取出在initmapping()中从配置文件中读取的每一个datasource,设置logwriter,如果为genericdatasource的实例,则打开数据源。然后,把每个datasource放入context中。
datasource.setlogwriter(scw);
((genericdatasource)datasource).open();
getservletcontext().setattribute(key,datasource);
initother() - 设置其它尚未初始化的的参数(content、locale、nocache),并发布formbeans、forwards、mappings到context:
getservletcontext().setattribute(action.form_beans_key, formbeans);
getservletcontext().setattribute(action.forwards_key, forwards);
getservletcontext().setattribute(action.mappings_key, mappings);
initservlet() - 初始化controller servlet的servlet mapping。这儿也使用了digester工具,扫描web.xml所有的<web-app/servlet-mapping>,寻找servlet-name与当前servlet相同的mapping,置入context。代码如下;
digester digester = new digester();
digester.push(this);
digester.setdebug(debug);
digester.setvalidating(validating);
digester.addcallmethod(“web-appservlet-mapping”,“addservletmapping”, 2);
digester.addcallparm(“web-appservlet-mappingservlet-name”, 0);
digester.addcallparm(“web-appservlet-mappingurl-pattern”, 1);
inputstream is = getservletcontext().getresourceasstream(“/web-infweb.xml”);
digester.parse(is);
getservletcontext().setattribute(action.servlet_key,servletmapping);
---------------------------------------------------------
ActionServlet在整个Struts框架中的作用及工作流程 之 我的理解
ActionServlet在整个Struts框架中的作用及工作流程 之 我的理解2007-01-03 15:14ActionServlet class 是整个struts 框架的骨干,也是主要的控制部件,它能够处理客户request 并且决定由哪个Action 处理 客户request。它象一个Action工厂,创建基于客户request清求的Action类的对象
ActionServlet class是一个简单的Servlet,就像其他的Servlet一样,它继承了javax.servlet.http.HttpServlet 并且实现了HttpServlet 的每一个生命方法,其中包括init(), doGet(), doPost(), and destroy() 。其中有两个主要的方法doGet() and doPost(). 它们的代码如下
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
process(request, response);
}
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
process(request, response);
}
这两个方法惊人的相似,它们都调用同一个单纯的方法process()。Struts的特效行为就是通过这两个方法开始的。process()方法处理所有的request并且还有下面的方法信号
protected void process(HttpServletRequest request,
HttpServletResponse response);
当ActionServlte 得到一个清求时,执行下列步骤
1.doGet(), doPost()收到一个请求并调用process()方法
2.process()方法得到当前RequestProcessor 类和调用RequestProcessor.process()方法
3.RequestProcessor.process()方法为当前得到的请求服务。本方法从struts-config.xml文件里重新得到<action>元素,<action>元素是用来匹配从request对象上提交过来的path。做以上事情是通过匹配传来的<html:form>中action元素和<action>中path元素是否相同。代码如下
<html:form action="/Lookup"
name="lookupForm"
type="ch04.LookupForm" >
<action path="/Lookup"
type="ch04.LookupAction"
name="lookupForm" >
<forward name="success" path="/quote.jsp"/>
<forward name="failure" path="/index.jsp"/>
</action>
4.RequestProcessor.process()匹配了<action>后,开始寻找<form-beans>中的name属性并和<action>中的name属性匹配。代码如下
<form-beans>
<form-bean name="lookupForm"
type="ch04.LookupForm"/>
</form-beans>
<action path="/Lookup"
type="ch04.LookupAction"
name="lookupForm" >
<forward name="success" path="/quote.jsp"/>
<forward name="failure" path="/index.jsp"/>
</action>
5.当RequestProcessor.process()知道了FormBean的充分有资格的名字时,它开始创建或者重新得到一个池功能的
ActionForm对象并以<form-beans>的type属性命名,然后用request传来的值复给自己的date mumber(属性)
6.ActionForm 的属性复完值后,RequestProcessor.process()调用ActionForm.validate() 方法
,用来检查传来的值是否有效。
7.当RequestProcessor.process()知道所有它需要的东西后,就开始服务这个请求。它做这些是通过用
Action的名字同<action>的type属性匹配,然后调用Action的execute()方法
8.当Action类从处理中返回后,它的excute()方法返回一个ActionForward对象。它是用来决定这个事务的目标。(view)
RequestProcessor.process()重新控制,并且request被转到别决定的目标(view)
它是Structs应用程序的核心,它是主要的控制组件,用于处理客户端请求,决定哪一个Action类处理每个接收到的请求。
ActionServlet类,实质就是一个简单的servlet,和HttpServlet很相似,它继承自HttpServlet类,并且也实现了HttpServlet的生命周期方法,init,doGet,doPost,destroy。
所有指定的行为都起源于ActionServlet类的process()方法,它处理所有的请求。
ActionServlet接收到请求后,按照下面的步骤走:
1)doPost或者doGet收到请求并调用process方法。
2)process方法取得当前的RequestProcessor,然后调用它的process方法。
3)RequestProcessor.process()方法是所有请求得到真正处理的地方。这个方法从struts-config.xml文件找到<action>元素,取得它符合提交请求的路经。这个路经一般是用<html:form/>标记的action属性指定的。
4)如果process方法找到匹配的<action>,就会找到<form-bean>(其中的name属性和<action>元素的name属性一致)。
5) process()方法知道FormBean的完整命名后,会创建或者取得一个ActionForm的缓冲实例,这个ActionForm的命名由<form-bean>元素的类型属性命名,然后将请求中提交的值填入这个实例中。
6) 填充完数据后,process方法调用ActionForm的validate方法,对提交的值进行验证。
7)这时,process方法就可以开始处理请求了。它从<action>元素的type属性取得完整的Action类名,然后创建这个类,然后调用这个Action子类的execute方法。
8)Action类从processing返回后,execute方法返回一个ActionForward对象,这个对象用于确定这个事务处理的目标。process方法继续控制,请求也会被转发到确定的目标。
9)到这里,ActionServlet实例就完成了它处理请求的过程,并且已经准备好了为将来的请求进行服务。
学习笔记使用struts-1.3.5-src.zip 的源码,
下载地址:http://archive.apache.org/dist/struts/source/
1. 在web.xml中通过下面定义把所有的*.do交给ActionServlet处理
<!-- Standard Action Servlet Configuration (with debugging) -->
<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,
/WEB-INF/struts-config-Wildcard.xml
</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<!-- Standard Action Servlet Mapping -->
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
2. 下面研究一下struts的源码,由于servlet设置了load-on-startup,所以tomcat启动时会加载ActionServlet,也就是会执行ActionServlet中的init()方法,Struts 的初始化实现就是在这里实现的。
注: 由于servlet的生命周期为 web容器加载和实例化类/init()初始化/service()请求处理/destroy()四个阶段,而init()方法在tomcat启动后只执行一次,所以如果想在tomcat启动后用debug模式查看ActionServlet中init()方法的执行,可以把上面的<load-on-startup>2</load-on-startup>注释掉就可以了(不过真正开发时还是需要的)。
3. 在ActionServlet中定义了一些常量,如下:
// 默认的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";
// ① initInternal();
// 一些文档类型定义,用来验证相应的配置文件如struts-config.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",
"-//Apache Software Foundation//DTD Struts Configuration 1.2//EN",
"/org/apache/struts/resources/struts-config_1_2.dtd",
"-//Apache Software Foundation//DTD Struts Configuration 1.3//EN",
"/org/apache/struts/resources/struts-config_1_3.dtd",
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN",
"/org/apache/struts/resources/web-app_2_3.dtd"
}; // ③ initServlet();
protected String servletMapping = null; // ③ initServlet();
protected String servletName = null; // ③ initServlet();
4. ActionServlet 中的init()方法执行流程如下
① 内部资源文件 ActionResources.properties 的初始化 initInternal();
protected MessageResources internal = null; // ① initInternal();
protected String internalName = "org.apache.struts.action.ActionResources"; // ① initInternal();
// initInternal 方法中通过下面得到一个MessageResources对象
internal = MessageResources.getMessageResources(internalName);
此资源文件主要包括一些消息信息的定义,具体可参考org.apache.struts.action下的ActionResources.properties文件
在MessageResources.java中的getMessageResources方法,
if (defaultFactory == null) {
defaultFactory = MessageResourcesFactory.createFactory(); // ⑴
}
return defaultFactory.createResources(config); // 传入internalName // ⑵
⑴
MessageResourcesFactory.createFactory() 所做的工作:
protected static transient Class clazz = null;
protected static String factoryClass =
"org.apache.struts.util.PropertyMessageResourcesFactory";
clazz = RequestUtils.applicationClass(factoryClass);
而RequestUtils.applicationClass通过classLoader加载一个
org.apache.struts.util.PropertyMessageResourcesFactory
⑵
defaultFactory.createResources(config) 所做的工作:
this.factory = factory;
("org.apache.struts.util.PropertyMessageResourcesFactory")
this.config = config;("org.apache.struts.action.ActionResources")
this.returnNull = returnNull;(true)
PropertyMessageResourcesFactory extends MessageResourcesFactory
返回一个MessageResources对象
② 调用 initOther(); 从web.xml中加载ActionServlet的初始化参数,包括config/ convertNull
protected String config = "/WEB-INF/struts-config.xml"; // ② initOther();
protected boolean convertNull = false; // ② initOther();
// 得到web.xml中"config"参数
String value;
value = getServletConfig().getInitParameter("config");
if (value != null) {
config = value;
}
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name> <!-- 得到"config"参数-->
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>convertNull</param-name> <!-- 得到"convertNull"参数-->
<param-value>true</param-value>
</init-param>
.......
</servlet>
// 获得convertNull的值(true/yes/on/y/1)
getServletConfig().getInitParameter("convertNull");
如果这个参数的值为 true (true/yes/on/y/1) , 数值型(BigDecimal/BigInteger/Boolean/Byte/Character/Double/Float/Integer/Long/Short)的Java 包装类(比如java.lang.Integer)的初始值为null,而非0。缺省值[false]
使其初始值为null的方法如下:
// 将所有的转换器注销掉
ConvertUtils.deregister();
// 为指定类型clazz注册转换器converter
ConvertUtils.register(new BigDecimalConverter(null), BigDecimal.class);
ConvertUtils.register(new BigIntegerConverter(null),BigInteger.class);
.......
注: ConvertUtils 用法如下
deregister () 和 deregister (java.lang.Class clazz)
注销转换器,前者将所有的转换器注销掉,后者只注销对应于clazz的转换器register( Converter converter, java.lang.Class clazz)
为指定类型clazz注册转换器converter。如果clazz已经存在一个对应的转换器,那么converter覆盖原来的转换器。
③ 调用 initServlet(); 从web.xml中加载ActionServlet的初始化参数如servlet-name,加载DTD文件并把其放入HashMap缓存,读取并解析web.xml的内容
// Remember our servlet name
getServletConfig().getServletName();
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name> <!-- 得到"config"参数-->
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>convertNull</param-name> <!-- 得到"convertNull"参数-->
<param-value>true</param-value>
</init-param>
.......
</servlet>
// Prepare a Digester to scan the web application deployment descriptor
Digester digester = new Digester();
// 把当前的 ActionServlet 对象放入到解析堆栈中
digester.push(this);
// 指明要考虑命名空间
digester.setNamespaceAware(true);
// 缺省值[false] ,解析器只是检查XML是否格式良好(well formed)
digester.setValidating(false);
// Register our local copy of the DTDs that we can find
// struts 可使用 struts-core-1.3.5.jar 包 中的DTD中来处理struts配置文件,这样可适用于那些没有连接到internet的应用环境
for (int i = 0; i < registrations.length; i += 2) {
URL url = this.getClass().getResource(registrations[i + 1]);
if (url != null) {
// 读取DTD文件并把其放入 HashMap 缓存
digester.register(registrations[i], url.toString());
}
}
/************************************************************
// 一些文档类型定义,用来验证相应的配置文件如struts-config.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",
"-//Apache Software Foundation//DTD Struts Configuration 1.2//EN",
"/org/apache/struts/resources/struts-config_1_2.dtd",
"-//Apache Software Foundation//DTD Struts Configuration 1.3//EN",
"/org/apache/struts/resources/struts-config_1_3.dtd",
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN",
"/org/apache/struts/resources/web-app_2_3.dtd"
}; // ③ initServlet();
************************************************************/
// Configure the processing rules that we need
// 运行时,digester 就会调用 ActionServlet中的 addServletMapping() 方法,并传入两个参数
digester.addCallMethod("web-app/servlet-mapping", "addServletMapping", 2);
digester.addCallParam("web-app/servlet-mapping/servlet-name", 0);
digester.addCallParam("web-app/servlet-mapping/url-pattern", 1);
得到
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
/************************************************************
// 来判断当前 servlet 名称是否为正在运行的 servlet 名称,如是,就把 url-pattern 作为 servletMapping
public void addServletMapping(String servletName, String urlPattern) {
if (servletName == null) {
return;
}
if (servletName.equals(this.servletName)) {
if (log.isDebugEnabled()) {
log.debug("Process servletName=" + servletName
+ ", urlPattern=" + urlPattern);
}
this.servletMapping = urlPattern;
}
}
************************************************************/
// 读取配置文件web.xml的内容
InputStream input = getServletContext().getResourceAsStream("/WEB-INF/web.xml");
// 如找不到/WEB-INF/web.xml文件,则报错
if (input == null) {
log.error(internal.getMessage("configWebXml"));
throw new ServletException(internal.getMessage("configWebXml"));
}
/************************************************************
// 报错信息定义在org/apache/struts/action/ActionResources.properties中
configWebXml=The /WEB-INF/web.xml was not found.
************************************************************/
// 解析input流文件,每读到一个节点元素就触发一个事件
digester.parse(input);
注: Digester 是一个基于 DOM 的 SAX 实现的类,它是事件触发的,可以将XML文件转换为任意的Java对象,支持规则的对任意XML文档的处理。原先是struts项目的一部分,后因其通用性而划归Commons子项目。
// 把servletMapping存储到servletContext中,属性名为Globals.SERVLET_KEY ( " org.apache.struts.action.SERVLET_MAPPING " )
if (servletMapping != null) {
getServletContext().setAttribute(Globals.SERVLET_KEY,servletMapping);
}
-----------------------------------------------------
java.lang.object
|
+--javax.servlet.genericservlet
|
+--javax.servlet.http.httpservlet
|
+--org.apache.struts.action.actionservlet
struts提供了一个缺省版本的actionservlet类,你可以继承这个类,覆盖其中的一些方法来达到你的特殊处理的需要。actionservlet继承与javax.servlet.http.httpservlet,所以在本质上它和一个普通的servlet没有区别,你完全可以把它当做一个servlet来看待,只是在其中完成的功能不同罢了。actionservlet主要完成如下功能:
将一个来自客户端的uri映射到一个相应的action类
如果是这个action类是第一次被调用,那么实例化一个并放入缓存
如果在配置文件(struts-config.xml)中指定了相应的actionform,那么从request中抓取数据填充formbean
调用这个action类的perform()方法,传入actionmapping的一个引用,对应的actionform、以及由容器传给actionservlet的httpservletrequest、httpservletresponse对象。
确省版本的actionservlet会从配置文件web.xml中读取如下初始化参数:
application
应用使用的资源包(resources bundle)的基类
factory
用于创建应用的messageresources对象的messageresourcesfactory的类名。确省是org.apache.struts.util.propertymessageresourcesfactory。
config
struts的配置文件,确省是/web-inf/struts-config.xml。注意这儿是与应用context关联的相对路径。
content
定义了确省的内容类型和编码格式,它会被自动地被设置到每个response中,如果jsp/servlet中没有明确的设置。确省是text/html。
debug
调试信息的级别。默认为0,比当前级别高的调试信息会被log到日志文件中。
detail
与debug的作用类似,只是这个detail是initmapping()时专用的。调试信息会被打印到system.out,而不是日志文件。
formbean
actionformbean的实现类,确省为org.apache.struts.action.actionformbean
forward
应用中使用的actionforward类,确省是org.apache.struts.action.actionforward。
locale
指定了确省使用的locale对象。设为true,当得到一个session时,会自动在session中存储一个以action.locale_key标示的locale对象,如果session中还没有与action.locale_key绑定的locale对象。
mapping
应用中使用的actionmapping类,确省是org.apache.struts.action.actionmapping。
multipartclass
文件上传使用的mutipartrequesthandler的实现类。确省为org.apache.struts.upload.diskmultipartrequesthandler
nocache
如果设为true,那么actionservlet会自动在每个到客户端的响应中添加nocache的html头,这样客户端就不会对应用中的页面进行缓存。确省为false
null
如果设置为true,那么应用在得到一个未定义的message资源时,会返回null,而不是返回一个错误信息。确省是true。
maxfilesize
文件上传的大小上限,确省为250m
buffersize
文件上传时的缓冲区的大小,确省为4m
tempdir
设置用于上传时的临时目录。工作目录会作为一个servlet环境(context)的属性提供。
validate
are we using the new configuration file format?确省为true。
validating
在解析配置xml文件是是否进行有效性的验证。确省为true
actionservlet中应用了命令设计模式。
一个servlet在由容器生成时,首先会调用init()方法进行初始化,在接到一个http请求时,调用相应的方法进行处理;比如get请求调用doget()方法,post请求调用dopost()方法。所以首先看看actionservlet的init()方法,你就会很清楚为什么actionservlet可以完成这些功能了。
init()
在它的init()方法中,actionservlet依次调用如下protected的方法完成初始化:
initactions() - 大家可能还曾有这个疑问:struts为什么可以找到一个请求uri对应的action类呢?答案就在这儿,actionservlet有一个actions属性,类型为org.apache.struts.util.fasthashmap,用于存储以类的全名为key的已实例化的action类。在init()时首先调用的就是initactions()方法,在这个方法中只是简单的清除map中的所有的名值对,
synchronized (actions) {
actions.setfast(false);
actions.clear();
actions.setfast(true);
}
首先把actions设为slow模式,这时对fasthashmap的访问是线程同步的,然后清除actions中的所有的已存在的名/值对,最后再把actions的模式设为fast。由于fasthashmap是struts在java.util.hashmap的基础上的一个扩展类,是为了适应多线程、并且对hashmap的访问大部分是只读的特殊环境的需要。大家知道java.util.hashmap是非线程安全的,所以hashmap一般适用于单线程环境下。org.apache.struts.fasthashmap就是继承于java.util.hashmap,在其中添加多线程的支持产生的。在fast模式下的工作方式是这样的:读取是非线程同步的;写入时首先克隆当前map,然后在这个克隆上做写入操做,完成后用这个修改后的克隆版本替换原来的map。那么在什么时候会把actions类添加到这个map中呢?我们已经提到了struts是动态的生成action类的实例的,在每次actionservlet接收到一个get或post的http请求时,会在这个map中查找对应的action类的实例,如果不存在,那么就实例化一个,并放入map中。可见这个actions属性起到了对action类实例的缓存的作用。
initinternal() - 初始化actionservlet内部使用的资源包messageresources,使用messageresources.getmessageresources(internalname)得到 internalname为"org.apache.struts.action.actionresources"对应的actionresources.properties文件。这个资源包主要用于actionservlet处理过程中的用到的提示信息,这儿不展开讨论。
initdebug() - 从web.xml中读取本应用的debug级别参数getservletconfig().getinitparameter("debug"),然后赋给debug属性。
initapplication()- 初始化应用资源包,并放置入servletcontext中。
string factory =getservletconfig().getinitparameter(“factory”);
string oldfacory = messageresourcesfactory.getfactoryclass();
if (factory !=null)
messageresourcesfactory.setfactoryclass(factory);
string value = getservletconfig().getinitparameter("application");
messageresourcesfactory factoryobject =
messageresourcesfactory.createfactory();
application = factoryobject.createresources(value);
messageresourcesfactory.setfactory(oldfactory);
getservletcontext().setattribute(action.messages_key, application);
说明:文中引用的代码片断可能会省略了一些例外检查等非主线的内容,敬请注意。
首先从配置文件中读取factory参数,如果这个参数不为空,那么就在messageresourcesfactory中使用这个指定的factory类;否则,使用默认的工厂类org.apche.struts.util.propertymessageresourcefactory。然后调用messageresourcesfactory的静态createfactory()方法,生成一个具体的messageresourcefactory对象(注意:messageresourcesfactory是抽象类)。这样就可以调用这个具体的messageresourcefactory的createresource()方法得到配置文件(web.xml)中定义的资源文件了。
上面的application对象类型为messageresources。在web.xml中在配置actionservlet时可以指定一个特定的工厂类。不能直接messageresourcesfactory的createresources()方法,因为这个方法是abstract的。创建factoryobject的过程如下:
messageresourcefactory factoryobject=
messageresourcesfactory.createfactory();
application = factoryobject.createresources(value);
<li>initmapping() - 为应用初始化mapping信息actionservlet有一个protected的属性:mapping,封装了一个actionmapping的对象集合,以便于管理、查找actionmapping。mappings是org.apache.struts.action.actionmappings类的实例。主要有两个方法:addmapping(actionmapping mapping)和findmapping(string path)。actionmapping也是使用上面提到的org.apache.struts.util.fasthashmap类来存储所有的actionmapping对象。
mappings.setservlet(this);
……
// initialize the name of our actionformbean implementation class
value = getservletconfig().getinitparameter("formbean");
if (value != null)
formbeanclass = value;
// initialize the name of our actionforward implementation class
value = getservletconfig().getinitparameter("forward");
if (value != null)
forwardclass = value;
// initialize the name of our actionmapping implementation class
value = getservletconfig().getinitparameter("mapping");
if (value != null)
mappingclass = value;
在initmapping()中,首先链接mappings对象到本servlet实例。其实这句话的作用很简单,在actionmappings中会有一个actionservlet类型的属性,这个属性就界定了这个actionmappings对象所属的actionservlet。struts的实现比较灵活,其中的actionformbean、actionforward、actionmapping类你完全可以使用自己实现的子类,来定制struts的工作方式。上面的代码就从配置文件(web.xml)中读取formbean、forward、mapping参数,这些参数就是你定制的actionformbean、actionforward、actionmapping类名。
// initialize the context-relative path to our configuration resources
value = getservletconfig().getinitparameter("config");
if (value != null)
config = value;
// acquire an input stream to our configuration resource
inputstream input = getservletcontext().getresourceasstream(config);
digester digester = null;
digester = initdigester(detail);
try {
formbeans.setfast(false);
forwards.setfast(false);
mappings.setfast(false);
digester.parse(input);
mappings.setfast(true);
forwards.setfast(true);
formbeans.setfast(true);
} catch (saxexception e) {
throw new servletexception
(internal.getmessage("configparse", config), e);
} finally {
input.close();
}
从web.xml读取struts的配置文件的位置。使用org.apache.struts.digester.digester解析config参数标示的配置文件,通常为“/web-inf/struts-config.xml”,解析出所有的data-source、form-bean、action-mapping、forward。从上面的程序片断看到,digester仅仅调用了一个parse()方法,那么,digester是怎样把解析struts-config.xml文件并把解析的结果form-bean等信息存储到属性变量formbeans等中的呢?你可以注意到在调用digester.parse(inputstream)之前,首先调用了initdigester()方法:
digester digester = new digester();
digester.push(this);
digester.addobjectcreate("struts-config/action-mappings/action",
mappingclass, "classname");
digester.addsetproperties("struts-config/action-mappings/action");
digester.addsetnext("struts-config/action-mappings/action",
"addmapping",
"org.apache.struts.action.actionmapping");
digester.addsetproperty
("struts-config/action-mappings/action/set-property",
"property", "value");
在这个方法中首先生成一个digester对象,然后设置解析的规则和回调,如果你对xml、sax不是很熟,这儿不必纠缠太深。要注意的是addsetnext()方法,设置了每一个要解析元素的set next回调方法,而这个方法就是由digester解析器的父提供的。上面的片断中的“addmapping”就是actionservlet本身定义的一个方法,将由digester回调。digester就是籍此把解析出的每一个formbean、actionforward、actionmapping等存储到属性变量formbeans、forwards、mappings等中的。
initupload() - 初始化有关upload的一些参数,比如:buffersize、tempdir。
initdatasource() -取出在initmapping()中从配置文件中读取的每一个datasource,设置logwriter,如果为genericdatasource的实例,则打开数据源。然后,把每个datasource放入context中。
datasource.setlogwriter(scw);
((genericdatasource)datasource).open();
getservletcontext().setattribute(key,datasource);
initother() - 设置其它尚未初始化的的参数(content、locale、nocache),并发布formbeans、forwards、mappings到context:
getservletcontext().setattribute(action.form_beans_key, formbeans);
getservletcontext().setattribute(action.forwards_key, forwards);
getservletcontext().setattribute(action.mappings_key, mappings);
initservlet() - 初始化controller servlet的servlet mapping。这儿也使用了digester工具,扫描web.xml所有的<web-app/servlet-mapping>,寻找servlet-name与当前servlet相同的mapping,置入context。代码如下;
digester digester = new digester();
digester.push(this);
digester.setdebug(debug);
digester.setvalidating(validating);
digester.addcallmethod(“web-appservlet-mapping”,“addservletmapping”, 2);
digester.addcallparm(“web-appservlet-mappingservlet-name”, 0);
digester.addcallparm(“web-appservlet-mappingurl-pattern”, 1);
inputstream is = getservletcontext().getresourceasstream(“/web-infweb.xml”);
digester.parse(is);
getservletcontext().setattribute(action.servlet_key,servletmapping);
---------------------------------------------------------
ActionServlet在整个Struts框架中的作用及工作流程 之 我的理解
ActionServlet在整个Struts框架中的作用及工作流程 之 我的理解2007-01-03 15:14ActionServlet class 是整个struts 框架的骨干,也是主要的控制部件,它能够处理客户request 并且决定由哪个Action 处理 客户request。它象一个Action工厂,创建基于客户request清求的Action类的对象
ActionServlet class是一个简单的Servlet,就像其他的Servlet一样,它继承了javax.servlet.http.HttpServlet 并且实现了HttpServlet 的每一个生命方法,其中包括init(), doGet(), doPost(), and destroy() 。其中有两个主要的方法doGet() and doPost(). 它们的代码如下
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
process(request, response);
}
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
process(request, response);
}
这两个方法惊人的相似,它们都调用同一个单纯的方法process()。Struts的特效行为就是通过这两个方法开始的。process()方法处理所有的request并且还有下面的方法信号
protected void process(HttpServletRequest request,
HttpServletResponse response);
当ActionServlte 得到一个清求时,执行下列步骤
1.doGet(), doPost()收到一个请求并调用process()方法
2.process()方法得到当前RequestProcessor 类和调用RequestProcessor.process()方法
3.RequestProcessor.process()方法为当前得到的请求服务。本方法从struts-config.xml文件里重新得到<action>元素,<action>元素是用来匹配从request对象上提交过来的path。做以上事情是通过匹配传来的<html:form>中action元素和<action>中path元素是否相同。代码如下
<html:form action="/Lookup"
name="lookupForm"
type="ch04.LookupForm" >
<action path="/Lookup"
type="ch04.LookupAction"
name="lookupForm" >
<forward name="success" path="/quote.jsp"/>
<forward name="failure" path="/index.jsp"/>
</action>
4.RequestProcessor.process()匹配了<action>后,开始寻找<form-beans>中的name属性并和<action>中的name属性匹配。代码如下
<form-beans>
<form-bean name="lookupForm"
type="ch04.LookupForm"/>
</form-beans>
<action path="/Lookup"
type="ch04.LookupAction"
name="lookupForm" >
<forward name="success" path="/quote.jsp"/>
<forward name="failure" path="/index.jsp"/>
</action>
5.当RequestProcessor.process()知道了FormBean的充分有资格的名字时,它开始创建或者重新得到一个池功能的
ActionForm对象并以<form-beans>的type属性命名,然后用request传来的值复给自己的date mumber(属性)
6.ActionForm 的属性复完值后,RequestProcessor.process()调用ActionForm.validate() 方法
,用来检查传来的值是否有效。
7.当RequestProcessor.process()知道所有它需要的东西后,就开始服务这个请求。它做这些是通过用
Action的名字同<action>的type属性匹配,然后调用Action的execute()方法
8.当Action类从处理中返回后,它的excute()方法返回一个ActionForward对象。它是用来决定这个事务的目标。(view)
RequestProcessor.process()重新控制,并且request被转到别决定的目标(view)
它是Structs应用程序的核心,它是主要的控制组件,用于处理客户端请求,决定哪一个Action类处理每个接收到的请求。
ActionServlet类,实质就是一个简单的servlet,和HttpServlet很相似,它继承自HttpServlet类,并且也实现了HttpServlet的生命周期方法,init,doGet,doPost,destroy。
所有指定的行为都起源于ActionServlet类的process()方法,它处理所有的请求。
ActionServlet接收到请求后,按照下面的步骤走:
1)doPost或者doGet收到请求并调用process方法。
2)process方法取得当前的RequestProcessor,然后调用它的process方法。
3)RequestProcessor.process()方法是所有请求得到真正处理的地方。这个方法从struts-config.xml文件找到<action>元素,取得它符合提交请求的路经。这个路经一般是用<html:form/>标记的action属性指定的。
4)如果process方法找到匹配的<action>,就会找到<form-bean>(其中的name属性和<action>元素的name属性一致)。
5) process()方法知道FormBean的完整命名后,会创建或者取得一个ActionForm的缓冲实例,这个ActionForm的命名由<form-bean>元素的类型属性命名,然后将请求中提交的值填入这个实例中。
6) 填充完数据后,process方法调用ActionForm的validate方法,对提交的值进行验证。
7)这时,process方法就可以开始处理请求了。它从<action>元素的type属性取得完整的Action类名,然后创建这个类,然后调用这个Action子类的execute方法。
8)Action类从processing返回后,execute方法返回一个ActionForward对象,这个对象用于确定这个事务处理的目标。process方法继续控制,请求也会被转发到确定的目标。
9)到这里,ActionServlet实例就完成了它处理请求的过程,并且已经准备好了为将来的请求进行服务。