1. Full action
这可以说是Struts action最流行的用法了。它包含一个action类和一个form bean,action mapping如下:
<action path = "/fullAction"
type = "com.acme.struts.MyAction"
name = "myForm"
input = "/WEB-INF/jsp/dataError.jsp">
<forward name="OK" path="/WEB-INF/jsp/viewResult.jsp"/>
<forward name="ERROR" path="/WEB-INF/jsp/busError.jsp"/>
</action>
1.1. Full action的调用顺序
1. Struts controller组件接受一个请求(request)
2. Struts 找出负责处理该请求的action mapping
3. 如果在指定范围内找不到该form bean的实例或者指定的范围是“request”类型,则创建一个form bean的实例,否则重用该form bean的实例。
4. 如果form bean定义了reset()方法,则调用(1)。
5. Struts利用request参数来填充form bean实例,利用form bean的mutators。(译者注:即set***方法。)(2)
6. 如果“validate”属性没有显式的设为“false”,则struts调用form bean的validate()方法。(3)
7. 如果validate()方法返回非空的ActionErrors对象,则控制流程会被转到action mapping中“input”属性标志的URI。(4a)(译者注:validate()方法返回类型为void,ActionErrors对象是放在request中的)
8. 如果validate()方法返回空的ActionErrors对象或者null,Struts就开始调用action类的excute()方法。(4b)
9. excute()方法返回的是一个ActionForward对象,Struts利用这个ActionForward对象来选择流程的下一个URI。(5)
1.2. 结论和使用提示
1.2.1. 如果ActionForm.validate()返回一个非空的ActionErrors对象,则action类的excute()方法是不会执行的,并且控制流程会被转到action mapping中“input”属性标志的URI。
1.2.2. 到action mapping中的“input”属性只允许转发(forward)URI。如果你需要重定向(redirect),使用Controller.inputForward属性,这个属性在struts1.1时引入,当“inputForward”属性设为true的时候表明“input”属性不是URI,而是Action.forward元素的名称。
译者注:Forward和Redirect的区别如下,但是上文redirect指的只是URI还是Action.forward元素,这两者是不同的。
Forward是container直接将request到一个别的组件,这种方式下,实际上也就是response之前,用户请求request对象可以经过多个组件处理。request的parameters会传递到新的组件,如果新的组件path也包含querystring,这部分参数也会加到request中,如果参数名重复,就覆盖旧的值。这种是Forward元素定义的forward对象缺省的转向方式。
Redirect方式利用的是HTTP协议中的redirect机制,让客户端发起新的请求,到新的URI。初始的参数和request上下文都不在了。只有ActionForward路径中定义的参数。这种方式需要在struts配置文件中,将Forward元素的redirect属性设置为true。
2. Form-only action
包含一个自定义的form bean 和一个标准的ForwardAction类(Struts内置的)。这种用法可以用于一个action只有两种结果,其中之一是错误。
<action path = "/formOnlyAction"
type = "org.apache.struts.actions.ForwardAction"
name = "myForm"
input = "/WEB-INF/jsp/error.jsp"
parameter = "/WEB-INF/jsp/viewResult.jsp"
/>
2.1. Form-only调用顺序
1. 如果在指定范围内找不到该form bean的实例或者指定的范围是“request”类型,则创建一个form bean的实例。否则重用该form bean的实例。
2. 如果form bean定义了reset()方法,则调用(1)。
3. Struts利用request参数来填充form bean实例,利用form bean的mutators。(译者注:即set***方法。)(2)
4. 如果“validate”属性没有显式的设为“false”,则struts调用form bean的validate()方法。(3)
5. 如果validate()方法返回非空的ActionErrors对象,则控制流程会被转到action mapping中“input”属性标志的URI。(4a)(译者注:validate()方法返回类型为void,ActionErrors对象是放在request中的)。
6. 如果validate()方法返回空的ActionErrors对象或者null,则控制流程会被转到action mapping中“parameter”属性标志的URI。(4b)
2.2. 结论和使用提示
1. 在这里没有放业务逻辑代码的action类,所有的客户代码都在form bean的reset() 或者validate()方法中。
2. validate()方法有两个作用:一是验证request参数,一是访问业务层。
3. 因为action mapping中没有包含“forward”元素,所以控制只能被转发,不能被重定向。
4. Form-only Action的一个可能的用处是reponse修饰,一个form bean能够从request中接受一个业务对象ID,然后从业务/持久层中查找该业务对象,填充form bean的字段,然后转发到一个JSP页面来显示数据。
5. 对于Form-only Action,配置成“session”范围是比较方便的,这样可以缓存数据。
3. Action class-only action
Struts不要求为actionmapping定义个formbean,如下。
<action path = "/actionOnlyAction"
type = "com.acme.struts.MyAction"
input = "/WEB-INF/jsp/error.jsp">
<forward name="OK" path="/viewResult.do"/>
<forward name="ERROR" path="/WEB-INF/jsp/error.jsp"/>
</action>
3.1. Action-class调用顺序
1. Action类的execute()方法被调用。
2. execute()方法返回一个ActionForward对象,其目标URI必须定义在forward元素里。
3.2. 结论和使用提示
1. 由于没有定义form bean ,所以struts传递给execute()方法的form参数是null。
2. 如果action类需要接受,则必须手工的从request中获得。所以这种方法只能用在没有或者很少需要从request中获得参数,否则还不如直接用form bean更方便些。
3. 控制可以被转发或者重定向到另一个action或者JSP页面。如果转发到JSP,要确保在制定范围内存在带有输出数据的form bean。
4. 这种方法不能用于HTML FORM,因为struts依赖于action mapping 中与此action关联的form bean 来定义此FORM。所以,你只能在链接(link)中使用此方法。
4. JSP-only action
这种方式比较少用,更紧凑,比较容易出错。被用于从另外一个action转发,或者有其他方法获得所要的数据。
<action path = "/actionOnlyAction"
type = "com.acme.struts.MyAction"
input = "/WEB-INF/jsp/error.jsp">
<forward name="OK" path="/viewResult.do"/>
<forward name="ERROR" path="/WEB-INF/jsp/error.jsp"/>
</action>
4.1. 调用顺序
1. Struts controller组件接受request。
2. 调用ForwardAction.execute()
3. ForwardAction.execute()转发控制到“parameter”参数定义的action。
4.2. 结论和使用提示
1. 这种方式没有用到form bean,所以不会创建一个 form bean,也不会初始化、填充数据和传递到action类中。
2. 虽然这种方式称作JSP-only,但是如果需要,也可以转发到action。
3. 由于这个操作中没有form bean被创建,所以JSP依赖的form bean来自于指定范围中先前创建的form bean。form bean可以在先前的action中创建并转发到此action中
4. 这种方式的使用的非常少。例如,它可以作为转换码来转发到另一个action。 当应用被编译和部署后,可以利用这种action转换操作。
5. Two actions, one form
Struts允许从一个action调用另一个action,这样就可能实现一个简单的责任链。
<action path = "/sourceAction"
type = "com.acme.struts.Action1"
name = "myForm"
input = "/WEB-INF/jsp/error1.jsp">
<forward name="OK" path="/targetAction.do"/>
</action>
<action path = "/targetAction"
type = "com.acme.struts.Action2"
name = "myForm"
input = "/WEB-INF/jsp/error2.jsp">
<forward name="OK" path="/WEB-INF/jsp/viewTarget.jsp"/>
</action>
5.1. 结论和使用提示
这种方式的顺序与full-action这种方式没有什么不同。问题是,struts会为目标action重复调用reset() 和validate()方法,并且也会为form bean 的属性重新赋值。要把form bean当作传输/命令对象,我们必须防止这些属性被重新赋值。
解决方式依赖于目标action是如何被调用的。如果目标action是被转发的(forward),我们可以在HttpRequest对象中设置一个令牌来区别是否是链中的第一个action:
l 在form bean 的reset()方法中检测令牌。如果令牌没有找到,则表示是链中的第一个action。如果令牌找到,就不要复位form bean的属性了,因为这是一个链action。
l 在request中放置一个令牌。这个必须在reset()方法中做,因为该方法总是被调用而不是象validate()方法。如果struts-config.xml 中,该action mapping的“validate”属性被设为“false”,validate()方法就不会被调用。
l 在每个mutator中判断,这个action是否是链中的第一个,如果不是,就不要改变属性值了。
l 同样的判断在validate()方法。
没有必要从request对象中除掉令牌,因为request对象会被自动回收。
如果使用重定向来调用目标action,就不能使用request令牌了。因为request对象在重定向后会被重新创建。可以使用如下方法:
l 读action mapping 的名称。这个只在你定义了严格的调用顺序规则后才有用。如果struts-config.xml变了,则你也需要跟着改变。
l 在session或者更高的范围内存放一个令牌,但要确保链中的最后一个action结束后要除掉这个令牌。
l 在返回对象的URI上附加一个令牌。在重定向结束后会作为一个HTTP参数传递给目标action。这种方法需要做一些额外工作,但是值得一试。
重要:为了可预见的行为,在所有的action中使用相同的范围。
6. Two actions, two forms
我们已经有了两个action,为什么不能拥有两个不同的form呢。
<action path = "/inputAction"
type = "com.acme.struts.Action1"
name = "inputForm"
input = "/WEB-INF/jsp/error1.jsp">
<forward name="OK" path="/outputAction.do" redirect="true"/>
</action>
<action path = "/outputAction"
type = "com.acme.struts.Action2"
name = "outputForm"
input = "/WEB-INF/jsp/error2.jsp">
<forward name="OK" path="/WEB-INF/jsp/viewTarget.jsp"/>
</action>
6.
1. 结论和使用提示
这个调用顺序和以前并没有什么不同,但是由于现在我们有了不同的form bean 和相应的实例,我们可以使编码更清晰。
这种设计可以用来处理web应用的输入和输出。源action接受请求,源form bean 验证输入数据。如果输入合法,则控制被重定向到输出action。Struts会重新给form bean的字段赋值,但是这里有个技巧:可以为字段定义不同的名称,或者更好一些,对输出form bean的字段只读,Struts使用accessors 和mutators去操纵form bean的字段,所以如果没有mutators就不能修改了。
由于是利用持久数据手工填写了输出form,所以就不需要保存输入form的值了。因此,就不需要转发,而是使用了重定向。这样,源action和目标action就解耦了。
这种方式的一个非常好的副作用是输出页面可以重新加载而不用重新运行输入action,避免了重复提交输入数据。