鄙人一点对struts1.2中ActionForm的理解
初学struts的人我认为首先应该把struts1.2学精,何为精,就是明白struts的控制流程以及它核心API的实现原理(比如 Action/DispatchAction/MappingDispatchAction/LookupDispatchAction /ActionForm的实现原理)然后自己会写一个“山寨版”的struts1.2,只有到达这样的程度你才能深刻体会struts1.2的MVC,也 才证明你学会了struts1.2,当你再学习struts2.0的时候会非常的容易,那么玩转struts1.2的关键就是能够玩转 ActionForm,在此我把自己学习struts1.2时ActionForm的使用及其实现原理贴出来晒一晒,还望各位不要见笑,给些意见。
ActionForm的应用
(一)、ActionForm的特性
1.创建一个form类必须继承于四个父类中的一个,要么继承ActionForm,要么继承ValidatorForm,要么继承ValidatorActionForm,要么继承
2.一个form类中的每一个属性都将和页面中form表单中的每一个表单元素一一对应
Example:
一个表单为:
<form>
<input type="text" name="username"></input>
<input type="password" name="password"></input>
<input type="text" name="email"></input>
</form>
一个与之对应的form类
public class UserForm extends ActionForm{
private String username;
private String password;
private String email;
private String address;
//下面省略getter和setter方法
}
一个引用了该form类的appAction:
<form-beans>
<form-bean name="userForm" type="form.UserForm"></form-bean>
</form-beans>
<action-mappings>
<action path="/test" type="action.TestAction" name="userForm" attribute="uf" scope="request"></action>
</action-mappings>
3.在引用了form-bean的action中name属性的值就是form-bean中name的值
4.这个userForm默认会被放在session中,使用scope可以指定存储该form对象的地方,自然attribute就是用来给存放在该范围中的form对象起的别名,如果不设置attribute
属性那么attribute默认的就使用name的值
(二)、普通HTML表单使用Form的工作原理
ActionServlet 对struts-config进行解析时,当解析到某个action中存在一个属性name,那么ActionServlet中的 RequestProcessor就会根据该name的值找到对应的form-bean然后创建一个对应的form类实例,放在我们定义的存储范围中,当 表单提交到该action对应的appAction之前也就是到达FC的时候,FC会做以下事情
1.根据路径找到对应的内存中存放着的配置对象中的action
2.根据action中的attribute属性,从session中得到一个对应的form实例
3.该form实例调用reset方法对自己进行清空
4.用表单中的值去填充该form实例
5.如果要该form要进行验证那么就该form就会调用validate方法按照我们规定的验证规则进行验证
(三)、struts表单使用Form的工作原理
1. 利用struts的HTML标签库定义的HTML元素其实是服务器端的java代码,java代码是编译型代码而HTML则是解释型代码,所以在使用 struts的HTML标签库定义的HTML元素要更加的严谨,只要某个元素甚至是某个属性没有定义对,那么编译就不能通过从而抛出异常,例如在使用 struts的HTML标签库定义表单的时候action属性是在编译的时就要被确定的如果action属性所定义的提交路径是空或者是错误,那么服务器 在编译的时候就会抛出500的异常,而不像普通HTML表单action属性是在提交的时候才确定的
2.原理跟普通HTML表单使用Form的工 作原理大同小异,不同在于当服务器对form表单进行编译的时候会向action所指定的地址发一个请求,看是否正确,所以这个时候其实就已经提交了一次 表单,当表单到达FC的时候跟上面做的几件事情中就第三件不同,不同在于表单和form中的值都将互相填充,这就是struts对表单的回添机制
Example:
表单为:
//先导入struts的html标签库
<html:form action="test.do" method="post">
<html:text property="username"></html:text>
<html:text property="salary"></html:text>
中文<html:multibox property="lang" value="zh"></html:multibox>
英文<html:multibox property="lang" value="en"></html:multibox>
俄语<html:multibox property="lang" value="ey"></html:multibox>
<html:submit value="click me"></html:submit>
</html:form>
form类为:
public class UserForm extends ActionForm{
private String username;
private double salary=1000.00;
private String[] lang;
//省略getter和setter方法
}
配置文件为:
<form-beans>
<form-bean name="userForm" type="form.UserForm"></form-bean>
</form-beans>
<action-mappings>
<action path="/test" type="action.TestAction" name="userForm" attribute="uf">
<forward name="success" path="/show.do"></forward>
</action>
<action path="/show" type="action.ShowAction" >
<forward name="success" path="/test.do"></forward>
</action>
</action-mappings>
经验:
1.显示页面之前服务器会向text.do发送一次请求,该请求也就是提交表单
2.当上面的请求到FC的时候会用form中的值去填充这个表单然后回显给用户所以用户看到的页面显示结果是salary文本框中有一个值为1000
3. 当用户再次提交表单,请求到达FC的时候服务器会首先检查你所提交的表单元素的值跟session中这个form与之对应的属性的值是否相同,如果相同就直接利用该form去填充表单,如果不相同就先把提交的表单元素的值赋给form中与之对应的属性再利用form去填充表单
4.该实例有一个问 题,就是当用户第一次选择了两种语言,然后提交,第二次用户一种语言都没选,然后提交,这时候回显的结果出人意料,复选框中的值既然是第一次用户所选择的 值,原因是,复选框有一个特性,如果用户什么都不选择,那么提交表单的时候就不提交复选框,如果我们使用get请求提交的话就能明显的看到这种情况表单提 交的属性只有username和salary两个,这两个元素即使没有值也会提交,就因为这一点,当表单到达FC的时候,服务器只看到提交的两个表单元素 username和salary,然后检查form中的属性也只会检查username和salary两个,而不会检查lang,如果表单中的 username和salary与form中与之对应的username和salary属性不一样,那么就先把表单中的username和salary值 赋给form中的username和salary属性,然后利用form去填充表单,如果一样,那么就利用原来的form去填充表单,在这两种情况下 form中的lang属性都将不会被检查更不用提改变,所以lang用的还是上一次的值,自然回显的时候form中的复选框的值就是上一次的值
5.解决这一问题可以有两种办法,在提交表单以后就把session中的form对象给删除掉,每一次提交都新创建一个form对象也就是在页面的末尾加一个
<%session.removeAttribute("uf")%>
第二种解决办法是在UserForm中覆盖父类的reset方法,该reset方法将在form填充之前做,这样我们就能在reset中把我们想要复位的属性先复位
当然利用第二种是最好的
5.我们可以利用struts的form回添机制进行回添操作,回添的时机是在form到达页面之前,可以在Action中来做也可以在reset中来做
(四)、Form与实体对象之间的关系
有的时候我们为了方便会把取到的form中的值直接拷贝到实体对象中去然后把实体对象再存储到数 据库中,这样给我们的编程带来了很多的方便,但前提是实体对象中需要拷贝的属性,form中要拷贝过去的属性,与form对应的表单元素他们三者必须一一 对应.这样我们就可以把表单中的值得到封装到form中然后再把form中与实体对象中属性相同的值拷贝到实体对象中
Example:
entity:
public class User{
private String name;
private String password;
private double salary;
private String address;
//省略getter和setter方法
}
form:
public class UserForm{
private String name;
private String password;
private String salary;
//省略getter和setter方法
}
表单:
<form>
<input type="text" name="name"></input>
<input type="password" name="password"></input>
<input type="text" name="salary"></input>
</form>
1.把表单中的值赋值给UserForm
2.把UserForm中的值拷贝到User对象中:
//下面这条语句是在action的某个方法中做的所以form直接可以用
BeanUtils.copyProperties(user,form);
3.将user对象存放在数据库中