??xml version="1.0" encoding="utf-8" standalone="yes"?>
q个时候,你可能会问自?“当控制器接受到一个请求的时候,它如何知道调用哪个Action实例?”控制器要通过查看h消息q用一l动?br />映射(action mapping)来做出决定。动作映是Struts配置信息(配置一个特D的XML文g中)的一部分。这个配|信息会在启动时加蝲到内存中
Q让Struts框架得以在运行时加以利用。每个action元素在内存中都被表示为org.apache.struts.action.ActionMappingcȝ实例?br />ActionMapping对象包含一个path属性,用来与外部请求的URI部分匚w。如?
<action
path="/login"
type="com.oreilly.struts.banking.action.LoginAction"
scope="request"
name="loginForm"
validate="true"
input="/login.jsp"
<forward name="Success" path="/action/getaccountinformation" redirect="true"/>
<forward name="Failure" path="/login.jsp" redirect="true"/>
</action>
q里的登录动作映把路径"/login"映射Ccom.oreilly.struts.banking.LoginActionq个ActioncR无ZӞ当控制器接受C个URI
路径中包?/login"字符串的hӞ׃调用LoginAction实例的execute()Ҏ(gu)。Struts框架q会使用映射来指出“动?完成后要让用?br />转向哪个资源?/p>
使用Struts ActionForm
Struts框架的ActionForm对象可用来在用户和业务层之间传输客户的输入数据。Struts框架会自动从h中收集输入数据,再将q些数据交给
一个用表单bean(form bean)的Action对象Q接着表单再交l业务层。ؓ了把表示层和业务层分d来,你不应该直接把ActionForm对象?br />l业务层Q而是应该使用由ActionForm对象得到的数据创建适当的DTO。下面的步骤说明了Struts框架如何处理每个h相应的ActionForm?br />?
1.查该动作的相应讄Q查看是否已l有某个ActionForm得到配置?br /> 2.如果对应q个动作配置了某个ActionFormQ则使用action元素中的name属性来查找表单bean的配|信息?br /> 3.查看是否已经创徏了一个ActionForm的一个实例?br /> 4.如果在适当的作用域内已l存在一个ActionForm实例Q而且q个实例的类型正是这个请求所需要的cdQ则重用q个实例?br /> 5.否则Q创建所需ActionForm的一个新实例Qƈ存储在适当的作用域中(由action元素的scope属性设|??br /> 6.调用ActionForm实例的reset()Ҏ(gu)?br /> 7.反复处理h参数Q如果参数名在ActionForm实例中具有对应的讄Ҏ(gu)(setter method),׃ؓ它填上该h参数的倹{?br /> 8.最后,如果validate属性的D|ؓtrue,则调用ActionForm实例的validate()Ҏ(gu)Qƈq回所出现的Q何错误?br />对Q何HTML面而言Q如果表单数据是以POSTҎ(gu)传输的,应该用ActionForm。必要时Q相同的ActionForm可以同时l多个页面用,?br />要HTML字段能和ActionForm对象的属?property)匚w可以了?br />Struts框架所提供的ActionFormcdC多个Ҏ(gu)Q但到目前ؓ止,最重要的两个方法就是reset()和validate():
public void reset(ActionMapping mapping,HttpServletRequest request);
public ActionErrors validate(ActionMapping mapping,HttpServletRequest request);
在Struts ActionFormcMQ这两个Ҏ(gu)的默认实现是不完成Q何的逻辑。你必须在自qActionFormcM覆盖q两个方法。控制器以请求中?br />值来填写ActionForm实例之前Q会先调用reset()Ҏ(gu)。reset()Ҏ(gu)l了ActionForm一个机会,可将其属性设|ؓ原来默认的状态。这一炚w
帔R要,因ؓ表单bean实例可能会由多个h׃n或者由好几个不同的U程所存取。不q,如果你是让好几页׃n一个ActionForm实例Q可?br />不会Ld现reset()Ҏ(gu)Q这样一来只要这个实例还在,属性的值就不会被重新设|。另一U做法就是实C自己的resetFields()Ҏ(gu)Q?br />在成功更C务之后,׃q个ActioncL调用此方法?br />当请求中所携带的值已l插入到ActionFrom实例之后Q控制器回调用validate()Ҏ(gu)。ActionForm应该对输入数据完成必要的验证工作Q然
后向控制器返回所到的Q何错误。业务逻辑验证应该在业务对象中而不是在ActionForm中来完成。在ActionForm中所q行的验证工作,?br />是表C的验证而已?br />一旦写好ActionFormcdQ你必须通知Struts应用E序有这些ActionForm存在Q告诉Struts应用E序哪个动作映射应该使用哪个ActionForm?br />q是在配|文件中讄的。第一步是Z的应用程序在配置文g中的form-beansD里配置所有的ActionForm。看下面的一个例?
<form-beans>
<form-bean
name="loginForm"
type="com.oreilly.struts.banking.form.LoginForm"/>
<form-bean
name="accountInformationForm"
type="org.apache.struts.action.DynaActionForm">
<form-property name="accounts" type="java.util.ArrayList"/>
</form-bean>
</form-beans>
每个表单bean的name属性必都是独一无二的,而且其type属性必d义一个Javac(扩展了Struts ActionFormc)的完全限定名。下一?br />是在一个或多个action元素中用在form-benasD里的某个form-bean名。如下:
<action
path="/login"
type='com.oreilly.struts.banking.action.LoginAction"
scope="request"
name="loginForm"
validate="true"
input="/login.jsp">
<forward name="Success" path="/action/getaccountinformation" redirect="ture>
<forward name="Failure" path="/login.jsp" redirect="true"/>
</action>
注意Q登录动作映的name正好和前面form-beansD里某个name属性相匚w?/p>
我们知道 Struts 的入口是 ActionServlet. org.apache.struts.ActionServlet cd Struts 应用E序中负责拦截工作。所有来自客户层的请求,在交l应用程序处理之前,都会先经q?/span> ActionServlet ?/span> ?/span> ActionServlet 的一个实例接受到一?/span> HttpRequest 对象Ӟ无论q是通过 doGet() Ҏ(gu)q是 doPost() Ҏ(gu)受到?/span> , 都会调用 process() Ҏ(gu)来处理该h?/span> ActionServlet ?/span> process() Ҏ(gu)如下所C?/span> :
Protected void process(HttpServletRequest request,HttpServletResponse response) throws IOException,ServletExcepion
{
RequestUtils.selectModule(request,getServletContext());
getRequestProcessor(getModuleConfig(request)).process(request,response);
}
管 process() Ҏ(gu)看v来ƈ不复杂,但是它调用的Ҏ(gu)却很复杂。首先,它会调用 org.apache.struts.util.RequestUtils cȝ静态方?/span> selectModule(), q把当前的请求和q个 Web 应用E序?/span> ServletContext 都传入该Ҏ(gu)?/span> selectModule() Ҏ(gu)的工作是?/span> request.getServletContext() q回的\径与每个配置应用模块的前~相匹配,从而选出处理当前h的应用模块?/span>
?/span> : 如果你只用到了一?/span> Struts 配置文g , 那么你就只会有一个应用程序,也就是默认的应用E序。ؓ了让默认的应用程序和应用模块能够单而一致的处理hQ默认应用程序可视ؓ一个应用模块。因?/span> , Mh只要不含应用E序的后~ (suffix), 都会被传送给默认的应用程?/span> , 由其处理?/span>
扩展 ActionServlet c?/span>
管 Struts 框架仍然允许你扩?/span> ActionServlet c,但是q么做的好处已大不入前,因ؓ它的大部分功能都已经攑ֈ新的 RequestProcessor c里了。如果你仍然x展自q ActionSerlvet c,那么只要创徏一个扩展了 ActionServlet 的类q|?/span> Struts 框架让它使用你的cd可以了。看以下一个覆盖了 init() Ҏ(gu)的类?/span>
Package dory.doo.framework;
import javax.servlet.ServeltException;
import javax.sertlvet.UnaviableException;
import org.apache.struts.action.ActionServlet;
import dory.doo.strutus.service.IStorefrontService;
import dory.doo.strutus.service.StorefrontServiceImpl;
import dory.doo.strutus.framework.util.IConstants;
import dory.doo.strutus.framework.exceptions.DatastoreException;
/**
* 扩展?/span> Struts ?/span> ActionServlet, 以此来完成你自己Ҏ(gu)的初始化工作
*/
public class ExtendedActionServlet extends ActionServlet
{
public void init() throws ServletException
{
// 定你先调用了超c?/span>
super.init();
// 初始化持久存储服?/span>
try
{
// 创徏服务接口的一个实?/span>
IStorefrontService serviceImpl=new StorefrontServiceImpl();
// 把服务对象存储到应用作用域类
getServletContext().setAttribute(IConstants.SERVICE_INTERPACE_KEY,serviceImpl);
}
catch(DatastoreException ex)
{
// 如果服务对象的初始化工作Z问题Q就关闭 web 应用E序
ex.printStackTrace();
throw new UnavailableException(ex.getMessage());
}
}
}
覆盖 init() Ҏ(gu)只是一个例?/span> , 你可以覆盖Q何你惌盖的Ҏ(gu) , 如果真的需要覆?/span> init() Ҏ(gu) , L(fng)定你先调用了 super.init() Ҏ(gu) , q样才会完成默认的初始化工作?/span>
要用自q ActionServlet 子类Q那么必d web.xml 修改如下 :
<servlet>
<servlet-name>MyActionServlet</servlt-name>
<servlet-class>dory.doo.framework. ExtendedActionServlet</servlet-class>
</servlet>
Struts 初始化过E?/span>
?/span> web.xml 文g中配|的初始化参数而定Q?/span> servlet 容器首次启动?/span> , 或者第一个对?/span> servlet 的请求来到时 ,servlet 容器׃加蝲 Stuts ?/span> ActionServlet. 无论是哪U情?/span> ( 也不是什么样?/span> Java Servlet) Q都一定要保证 inint() 得到调用Q而且必须?/span> servlet 处理Mh之前完成?/span> Struts 框架?/span> init() 被调用时 , 会做好所有的初始化工作。看看如下就知道了它到底q了些什?/span> :
1. ?/span> Struts 框架内部的消息资源包q行初始化。这些消息资源包是用来向日志文g传输信息性,警示和错误消息的 . ?/span> org.apache.struts.action.ActionResources 资源?/span> ( ?/span> /org/acache/struts/action/ActionResources.properties 文g ) 则用来取得这些内部消息?/span>
2. ?/span> web.xml 文g加蝲控制 ActionServlet cd行为的初始化参数。这些参数包?/span> config,debug,detail 以及 convertNull ?/span>
3. ?/span> web.xml 文g加蝲 servlet 名和 servlet 映射信息Qƈq行初始化。这些值可供整?/span> Struts 框架使用 ( 几乎都是l?/span> JSP 标记库?/span> ), 以便?/span> HTML 表单提交Ӟ输出正确?/span> URL 目标。在此初始化期间Q?/span> Struts 框架l所有?/span> DTD 也会得到注册?/span> DTD 接下来可以用于严正配|文件?/span>
4. 加蝲默认应用E序?/span> Struts 配置数据Qƈq行初始化,q些配置数据?/span> config 初始化参数来指定。默认的 Struts 配置文g得到解析后,会创Z?/span> ApplicationConfig 对象Qƈ存储?/span> ServletContext 对象中。默认应用程序的 ApplicationConfig 对象会以 org.apache.struts.action.APPLICATION q个键值存储在 ServeltContext 对象中?/span>
5. Struts 配置文g中ؓ默认应用E序指定的每个消息资源都会被加蝲Q初始化Qƈ存储?/span> ServletContext 对象中适当位置 ( 依每?/span> message-resources 元素中指定的 key 属性而定 ) 。如果没有指?/span> key 属性,那么相应的消息资源会键?/span> org.apache.struts.action.MESSAGE 来存储。只有一个消息资源可作ؓ默认消息资源存储Q因为每个键都必L唯一的?/span>
6. Struts 配置文g中所声明的每个数据源都被会被加蝲q初始化。如果没有指定Q?/span> data-sources 元素Q则会蟩q这个步骤?/span>
7. Struts 配置文g中所指定的每?/span> plug-in( 插g ) 元素都会被加载和初始化。对于每?/span> plug-in 元素所指定的类Q其 itit() Ҏ(gu)都将被调用?/span>
8. 一旦默认应用程序正地完成了初始化之后Q?/span> servlet ?/span> init() Ҏ(gu)会确定是否指定了M其他的应用模块,如果有的话,对于每个应用模块都要重复步骤 4-7
?/span> : Z辑ֈ更好的性能Q你可能会尝试ؓ一个应用程序徏立多?/span> Struts 控制?/span> servlet 。这么做几乎得不到更好的性能或扩展性, Struts 设计者也不认是一个好注意?/span> Servlet 是多U程的,因此可以让多个客户同时执行。一?/span> servlet p同时为多个客h供服务?/span>