This article discusses different combinations of a Struts action class and a form bean and how these combinations can be used.
本文讨论了Struts框架中Action以及Form Bean之间的不同组合方式以及这些组合的使用。
Full action(一个完整的Action)
This is arguably the most popular form of Struts action. It contains an action class and a form bean. The action mapping for this operation looks like this:
首先这里要讨论的是Struts框架中最常用的组合,即一个Action对应一个Form Bean。这样的对应关系可以用下图来解释:
Full action call sequence
- Struts controller component receives a request.
- Struts identifies the action mapping which is responsible for request processing.
- Struts creates a new instance of the form bean, if it was not found in the scope or if the scope has "request" type. If the bean exists in the scope, it is reused.
- If form bean defines reset() method, it is called (1)
- Struts populates the fields with request arguments, using mutators (2)
- Struts calls validate() method if "validate" attribute of action mapping is not set to "false" (3)
- If validate() returns non-empty ActionErrors object, control is forwarded to an URI identified by "input" attribute of the action mapping (4a)
- if validate() returns empty ActionErrors object or null, Struts calls execute() method of the action class (4b)
- execute() method returns an ActionForward object, which is used by Struts to select the destination URI (5)
- Struts框架的控制器组件接收到一个Request请求;
- Struts框架通过Action Mapping配置来确定处理这个请求的Action;
- 如果在可见域(scope)内不存在该Action对应的Form Bean或者可见域为Request时,Struts框架创建一个新的(该Action对应的)Form Bean的实例。如果在可见域内存在此Form Bean,则可以重用;
- 如果在Form Bean中定义了reset()方法,则调用该方法;(1)
- Struts框架以Request域中的变量自动填充Form Bean中的变量,使用mutators;(2)
- 如果配置文件(struts-config.xml)Action-Mapping对应的validate属性被设置为True,那么Struts框架将自动调用validate()方法;(3)
- 如果Validate()方法返回一个非空的ActionErrors对象,Struts控制器会将页面导向至配置文件中定义个某个处理错误的页面;
- 如果Validate()方法返回的ActionErrors对象为空,Struts框架将会自动调用Action的Execute()方法;(4b)
- Execute()方法将会返回一个ActionForward的对象,Struts框架会通过这个对象决定页面的转向地址。(5)
Consequences and usage tips
- execute() method of an action class is not called if ActionForm.validate() returns non-empty ActionErrors object. Instead, control is directly routed to the URI defined in "input" attribute of action mapping.
- "input" attribute of action mapping allows forwarding to an URI only. If you need to redirect, use Controller.inputForward property. This property was introduced in Struts 1.1. When it set to "true" it indicates that Action.input is not a URI, but a name of an Action.forward element.
- 如果Action对应Form Bean的validate()方法返回的是一个非空的ActionErrors对象,那么Action的Execute()方法将不会被调用。此时,Struts框架将直接把页面导向Action Mapping中input属性定义的错误处理页面;
- Action Mapping中“Input”属性指定的仅为一个Form Bean的URL路径。如果需要重定向页面,应当使用Controller.inputForward属性(该属性在Struts1.1版本中引入)。当该属性被设为“True”时,表明Input属性设置的不是URL而是一个Action.forward元素名。
Form-only action
This action combines a custom form bean and the standard ForwardAction class. This type of action can be used when an action has only two outcomes, one of which is an error.
Form-only call sequence
- Struts creates a new instance of the form bean, if it was not found in the scope or if the scope has "request" type. If the bean exists in the scope, it is reused.
- If the form bean defines reset() method, it is called (1)
- Struts populates the fields with request arguments, using mutators (2)
- Struts calls validate() method if "validate" attribute of action mapping is not set to "false" (3)
- If validate() returns non-empty ActionErrors object, control is forwarded to an URI identified by "input" attribute of the action mapping (4a)
- if validate() returns empty ActionErrors object or null, control is forwarded to an URI identified by "parameter" attribute of the action mapping (4b)
Consequences and usage tips
- There is no action class to put business code in, so all custom code must be called from either reset() or validate() methods of the form bean.
- validate() performs two functions: validating request arguments, and accessing the business layer.
- Because action mapping does not contain "forward" elements, control can be only forwarded, but cannot be redirected.
- One of the possible usages of a form-only action is response rendering. A form bean can receive a business object id with a request, then look up the business object in the business/persistence layer, fill in the form fields and forward to a JSP which would display the result.
- It may be convenient to use "session" scope for form-only actions, thus implementing caching of output data.
Action class-only action
Struts does not require to declare a form bean for an action mapping. Hence the seemingly tautological action-only action.
Action-class only call sequence
- execute() is called on the action class (1)
- execute() returns ActionForward object; target URI must be defined in a "forward" element of action mapping (2)
Consequences and usage tips
- This action does not declare a form bean, thus Struts passes null to execute() method instead of a form bean.
- If the action receives request parameters, they must be retrieved from the request object manually. Action class-only action may be used if just a few or no parameters are passed with request, otherwise it is easier to use a form bean.
- Control may be either forwarded or redirected to another action or JSP page. If you are forwarding control to a JSP, make sure that necessary form beans with output data exist in the scope.
- Action-class only action cannot be called from HTML FORM, because Struts expects to find the form bean definition in an action mapping which serves the FORM. As a workaround, you can call an action-class only action from a link.
JSP-only action
This is the most compact, most error-prone and the least useful type of action. It can be used if it is forwarded to from another action, or if there are other means to obtain needed data.
The call chain
- request is received by Struts controller component
- Struts calls ForwardAction.execute()
- ForwardAction.execute() forwards control to an action defined in "parameter" attribute of the action mapping.
Consequences and usage tips
- This action does not use a form bean, thus it is not created, not initialized, not populated and not passed to an action class.
- Though I called it JSP-only operation, it can forward to any URI including other action if needed.
- Because a form bean is not instantiated during the course of this operation, JSP relies on a form bean which was created earlier and is in the scope of the action. Form bean could be created in another action which forwards control to this action, or it could be created with "session" or higher scope during processing of another request.
- Usage of this action is very limited. For example, it can be used as a switching yard to forward to another action. After application is compiled and deployed, the course of operation can be switched in the config file using this action.
Two actions, one form
Struts allows to call one action from another, thus it is possible to implement a simple Chain of Responsibility.
Two-action call sequence and usage tips
The call sequence does not differ from the full-action case. The problem is, that Struts calls reset() and validate() methods again for the target action, and it populates form bean fields as well. To use form bean as a transport/command object we must prevent overwriting of form bean fields.
The solution depends on how the target action is called. If the target action is forwarded to, we can use a token in the HttpRequest object to distinguish if an action is the first action in the chain.
- Check for the token in the reset() method of the form. If token not found, then this action is the first in the chain. If token is found, do not reset form fields, because this is a chained action.
- Plant a token into the request object. This must be done in reset() method, because it is always called unlike validate(). validate() may be not called if "validate" property is set to "false" in struts-config.xml file.
- Verify in each mutator, that the action is first in chain. If not, do not allow changing the field value. This prevents Struts from modifying the form fields. •
- check for token in validate() method. If not present, validate form fields, because this is the first action in chain.
You do not need to remove the token from the request object, the request will be disposed automatically.
If you use redirection to call the target action, you cannot use request token, because request object is recreated after redirection. The following solutions are possible:
- Read action mapping name. This works only if you have strict rules defining when each action is called. If struts-config.xml is changed, the code would have to be updated.
- Store a token on the server in the object with session or higher scope. The token should be promptly removed when the last action in chain finishes.
- Stick a token into the target URI of the response object, it would be passed to a target action as an HTTP request parameter after redirect completes. This solution takes some additional effort, but the result worth it.
Important: use the same form scope in both actions for predictable behavior.
Two actions, two forms
As long as we already have two actions, why not to have two different forms.
Two-action, two form usage tips
The call sequence does not differ from the previous case. But because now we have two different form types and different instances, we can make the code much cleaner.
This design can be used to handle input and output of web applications. The source action receives the request, the source form validates the input data. If input is valid, the control is redirected to output action. Struts would want to populate form bean fields again, but here is the trick: you can either define fields with different names, or even better, you can define only accessors for the output form bean. Struts uses accessors and mutators to operate on form bean fields, so without mutators it would not be able to modify the fields of the output form.
The control flow looks like this:
- Struts calls reset() on the input form bean (1)
- Struts populates the fields of input form bean using mutators (2). Input form bean does not even have to define the accessors.
- truts calls validate() on input form bean, which validates request data (3)
- If validate() returns non-empty ActionErrors object, control is forwarded to an error page identified by "input" attribute of the action mapping (4a)
- if validate() returns empty ActionErrors object or null, Struts calls execute() method of the input action class (4b)
- execute() updates business and persistent objects.
- execute() creates new ActionForward instance with modified URI of the target action, adding to it the ID of an object which must be displayed.
- if execute() returns "OK", Struts redirects to the output action (5)
- Struts calls reset() on the output form bean (6)
- Struts tries to populate the fields of the output form bean, but because there are no mutators defined, they are not changed (7)
- validate() has nothing to validate in the output form, so it returns null (8)
- execute() method of the action class locates business object using ID passed with the request, fills out the fields of the output form with business data and returns "OK" (9b)
- Struts displays the result page (10)
Because I fill out the output form manually with the persistent data, I do not need to preserve values of the input form bean. Thus, I do not need to use forwarding, and I can employ redirection instead. This way, input action and output action become less tied to each other.
A very nice side effect of this solution is that output page can be easily reloaded without processing the request in input action again. This helps to prevent double submission of input data.
More on double submit problem and how it can be defeated see in my other article: Redirect After Post
About the Author
Michael holds an MS in Computer Science from Moscow Aviation Institute (technical university), Moscow, Russia. He has more than 10 years of experience developing applications for MS-DOS, Windows and Java platform. he has devoted the last 5 years to server-side Java applications. Curently he is employed as a software engineer at International Lottery and Totalizator, Inc.
posted on 2005-10-24 22:17
Sung 阅读(1409)
评论(0) 编辑 收藏 所属分类:
Struts