- 浏览: 303228 次
最新评论
-
nucleus:
为什么都是没有图的。。。。。。。求图片啊
《研磨struts2》第七章 值栈和OGNL 之 7.4 ActionContext和ServletActionContext -
nucleus:
为什么都是没有图的。。。。。。。求图片啊
《研磨struts2》第七章 值栈和OGNL 之 7.4 ActionContext和ServletActionContext -
nucleus:
为什么都是没有图的。。。。。。。求图片啊
《研磨struts2》第七章 值栈和OGNL 之 7.4 ActionContext和ServletActionContext -
nucleus:
为什么都是没有图的。。。。。。。求图片啊
《研磨struts2》第七章 值栈和OGNL 之 7.4 ActionContext和ServletActionContext -
hunanjun000:
《研磨struts2》第四章 Action 之 4.5 Action的其它重要知识
4.3 Action的数据
4.3.1 数据来源
在helloworld示例里面,在运行Action的execute方法的时候,你会神奇般的发现,Action的属性是有值的,而这正是Action进行请求处理所需要的数据。那么,这些数据从何而来呢?
很明显,这些数据就是你在登录页面填写的数据,换句话说,这些数据来源于用户请求对象,也就是request对象。
可是,Struts2怎么知道,页面上的值如何和Action的属性进行对应呢?
这就涉及到如何把页面的数据和Action进行对应的问题了,接下来就来讨论页面的数据和Action的三种基本对应方式。
4.3.2 基本的数据对应方式
在Struts2中,页面的数据和Action有两种基本对应方式,分别是:属性驱动(FieldDriven)和模型驱动(ModelDriven)。
属性驱动又有两种情况:一种是基本数据类型的属性对应;另外一种是JavaBean风格的属性对应。为了区分它们,我们约定称呼如下:称呼“基本数据类型的属性对应”为属性驱动,而“JavaBean风格的属性对应”为直接使用域对象。
下面就分别来看看它们都什么意思,都如何实现。
1:属性驱动FieldDriven(基本数据类型的属性对应)
基本数据类型的属性对应,就是web页面上要提交的html控件的name属性,和Action的属性或者与属性相应的getter/setter相对应,这种做法就是基本数据类型的属性对应的属性驱动。
事实上,我们已经使用过这种方式了,前面HelloWorld示例,就是采用的这种方式来把值对应到Action中的。
比如在登录页面上,我们是这么写的:
在Action中是这么写的:
- public class HelloWorldAction extends ActionSupport {
- private String account;
- private String password;
- private String submitFlag;
- public String getAccount() {
- return account;
- }
- public void setAccount(String account) {
- this.account = account;
- }
- public String getPassword() {
- return password;
- }
- public void setPassword(String password) {
- this.password = password;
- }
- public String getSubmitFlag() {
- return submitFlag;
- }
- public void setSubmitFlag(String submitFlag) {
- this.submitFlag = submitFlag;
- }
- //其他部分暂时省略掉,好让大家看清楚数据的对应关系
- }
你会发现,在页面上input的name属性,和Action的属性是同一个名称,这样一来,当页面提交的时候,Struts2会自动从request对象里面把数据取出来,然后按照名称进行对应,自动设置到Action的属性里面去。
有些朋友可能会说,Action的属性都是private的呀,按道理外部是无法访问的,正是因为如此,才为每个私有的属性提供了getter/setter方法,来让外部访问。
这也意味着,如果你不想为每个属性提供getter/setter方法,觉得很累赘,有一个简单的方式,那就是把属性的可访问权限设置成public的就可以了。但在Java开发中,不是很建议直接开放属性让外部访问,一般都是通过getter/setter方法来访问。当然如何选择,根据实际情况来判断吧,总之两种方式都是可以把值对应上的。
2:属性驱动FieldDriven(直接使用域对象)
仔细察看上面属性驱动的方式,会发现,要是需要传入的数据很多的话,那么Action的属性也就很多了,再加上对应的getter/setter方法,Action类就直接上百行了,再在里面写请求处理的代码,会显得Action非常零乱,不够简洁,而且给人的感觉是Action的功能也不够单一。那么该怎么解决这个问题呢?
很简单,把属性和对应的getter/setter方法从Action里面移出去,单独做成一个域对象,这个对象就是用来封装这些数据的,然后在Action里面直接使用这个对象就可以了。
(1)先看看域对象的写法,按照JavaBean的风格来写,示例代码如下:
- public class HelloWorldModel {
- private String account;
- private String password;
- private String submitFlag;
- public String getAccount() {
- return account;
- }
- public void setAccount(String account) {
- this.account = account;
- }
- public String getPassword() {
- return password;
- }
- public void setPassword(String password) {
- this.password = password;
- }
- public String getSubmitFlag() {
- return submitFlag;
- }
- public void setSubmitFlag(String submitFlag) {
- this.submitFlag = submitFlag;
- }
- }
(2)看看此时,Action写法的变化,主要就是直接使用这个对象,其实就是定义一个属性是这个对象类型,然后为这个属性提供相应的getter/setter方法即可,当然也可以直接把这个属性的可访问属性设置成public,这样就不需要写getter/setter方法了。
原来Action里面直接使用属性值的地方,就修改成使用这个属性对象来获取值了。示例代码如下:
- public class HelloWorldAction extends ActionSupport {
- private HelloWorldModel hwm = new HelloWorldModel();
- public HelloWorldModel getHwm() {
- return hwm;
- }
- public void setHwm(HelloWorldModel hwm) {
- this.hwm = hwm;
- }
- public String execute() throws Exception {
- //1:收集参数,不用做了,数据会直接映射到上面的hwm里面
- //2:组织参数,也不用作了,数据会映射到上面的hwm的时候,就已经组织好了
- //3:调用模型的逻辑功能处理,这里不需要,只是简单的输出一下传入的参数
- this.businessExecute();
- //4:根据逻辑处理的结果来选择下一个页面,这里直接选择转向欢迎页面
- return "toWelcome";
- }
- public void validate(){
- if(hwm.getAccount()==null || hwm.getAccount().trim().length()==0){
- this.addFieldError("account", this.getText("k1"));
- }
- if(hwm.getPassword()==null || hwm.getPassword().trim().length()==0){
- this.addFieldError("password", this.getText("k2"));
- }
- if(hwm.getPassword()==null || hwm.getPassword().trim().length()<6){
- this.addFieldError("password", this.getText("k3"));
- }
- }
- /**
- * 示例方法,表示可以执行业务逻辑处理的方法,
- */
- public void businessExecute(){
- System.out.println("用户输入的参数为==="+"account="+hwm.getAccount()+",password="+hwm.getPassword()+",submitFlag="+hwm.getSubmitFlag());
- }
- }
(3)Action发生变化后,登录页面上也需要相应改变,否则数据是无法正确对应的,主要是在相应的name属性上,添加一个域对象的前缀,指明这个值到底对应到哪一个域对象里面去,示例如下:
同理欢迎页面也需要相应调整,示例如下:
好了,去测试一下看看,是否好用。
3:模型驱动ModelDriven
在Struts2中,还有另外一种对应数据的方式叫模型驱动ModelDriven。它的基本实现方式是让Action实现一个ModelDriven的接口,这个接口需要我们实现一个getModel的方法,这个方法返回的就是Action所使用的数据模型对象。
(1)把Action代码修改成ModelDriven的实现方式,只是添加了ModelDriven的实现,另外去掉了“hwm”属性对应的getter/setter方法,其他地方基本上没有什么变化,示例代码如下:
- import com.opensymphony.xwork2.ActionSupport;
- import com.opensymphony.xwork2.ModelDriven;
- public class HelloWorldAction extends ActionSupport implements ModelDriven{
- private HelloWorldModel hwm = new HelloWorldModel();
- public Object getModel() {
- return hwm;
- }
- public String execute() throws Exception {
- this.businessExecute();
- return "toWelcome";
- }
- public void validate(){
- if(hwm.getAccount()==null || hwm.getAccount().trim().length()==0){
- this.addFieldError("account", this.getText("k1"));
- }
- if(hwm.getPassword()==null || hwm.getPassword().trim().length()==0){
- this.addFieldError("password", this.getText("k2"));
- }
- if(hwm.getPassword()==null || hwm.getPassword().trim().length()<6){
- this.addFieldError("password", this.getText("k3"));
- }
- }
- /**
- * 示例方法,表示可以执行业务逻辑处理的方法,
- */
- public void businessExecute(){
- System.out.println("用户输入的参数为==="+"account="+hwm.getAccount()+",password="+hwm.getPassword()+",submitFlag="+hwm.getSubmitFlag());
- }
- }
(2)登录页面也需要做相应调整,主要就是去掉刚才给name属性添加的“hwm.”这个前缀,示例代码如下:
同理去调整欢迎页面,这里就不去示范了。
那么这里为什么不需要前缀了呢?
原因很简单,使用ModelDriven的方式,一个Action只能对应一个Model,因此不需要添加前缀,Struts2就能够知道,页面上“account”的值就对应到这个Model的“account”属性。如果你去加上前缀,反而对应不上了。
4:小结
(1)这里学习了三种数据的对应方式,在实际开发中该如何选择呢?
下面简要分析一下:
- 属性驱动(基本数据类型的属性对应):优点:简单,页面name和属性直接对应;缺点:导致Action类看上去比较零乱,显得功能不够单一。因此在实际开发中会酌情使用。
-
属性驱动(直接使用域对象):优点:把模型数据从Action中分离出来,让Action专注于请求处理,使得程序结构更清晰;缺点:页面上在对应的时候,必须添加正确的前缀,稍嫌麻烦。
但正是因为有前缀,在一个Action有多个数据模型的时候,这个缺点反而变成了优点,因为可以根据前缀来区分到底把这个数据对应给谁,这样一来,就不会乱了,比如:“hwm.uuid”、“um.uuid”就表示hwm和um这两个模型里面都有一个uuid的属性,但是,现在是带着前缀来指定值的对应,就不会出错了。在实际开发中,推荐优先使用这个方式。 - 模型驱动:优点:把模型数据从Action中分离出去了,使得程序结构更清晰;缺点:需要Action实现特殊的接口,而且把模型数据和Action作了一个绑定,这极大地限制了一个Action对应多个数据模型的能力,当然也可以做到,就是在这个模型里面包含其他的数据模型。在实际开发中,根据情况来选用。
(2)又有新问题了,这三种方式能不能混合使用呢?如果能?会不会冲突呢?
事实上,这三种方式是可以混合使用,甚至是三种方式一起使用。但是属性驱动(基本数据类型的属性对应)和模型驱动是有可能冲突的,因为这两种对应方式都没有前缀,如果出现这种冲突的情况,那么优先模型驱动的对应方式。
还是举个例子来说明,如果在Action中同时出现三种方式,示例代码如下:
- public class HelloWorldAction extends ActionSupport implements ModelDriven{
- /**
- * 用于ModelDriven使用
- */
- private HelloWorldModel hwm = new HelloWorldModel();
- /**
- * 用于域对象的方式使用
- */
- public HelloWorldModel hwm2 = new HelloWorldModel();
- /**
- * 用于FieldDriven使用
- */
- public String account = "";
- public Object getModel() {
- return hwm;
- }
- public String execute() throws Exception {
- System.out.println("模型驱动的值:account="+hwm.getAccount()+",password="+hwm.getPassword()+",submitFlag="+hwm.getSubmitFlag());
- System.out.println("使用域对象的值:account="+hwm2.getAccount()+",password="+hwm2.getPassword()+",submitFlag="+hwm2.getSubmitFlag());
- System.out.println("属性驱动的值:account="+account);
- return "toWelcome";
- }
- }
此时登录页面修改成如下示例:
注意,在上述页面的写法中,name="submitFlag"的值,将会使用ModelDriven的方式对应,因为没有其他可供它对应的地方;name="account"的值,既可以对应到Action的account属性,也可以通过ModelDriven的方式对应到hwm的account属性;而name="hwm2.password"的值,只能按照域对象对应的方式,对应到hwm2里面的password属性去。
去运行一下,看看结果。结果示例如下:
你会发现,password直接对应到了域对象的password去,毫无争议;而account的值,虽然可以同时对应到模型和属性上,但结果很明显是模型驱动优先,也就是对应到模型的account属性去了。
(3)学到这里,已经掌握了Action类的写法,掌握了Action里面execute方法的写法,也掌握了如何把值跟Action对应起来,看起来,知识好像足够多了。
但是在实际开发中,往往不会像前面示例得这么简单,而是需要面对各种复杂的情况,比如:
- 传入值的类型不一致,需要转换
- 需要传入一组数目不确定的字符串。这在web开发中是非常常见的,比如在注册用户的时候,可能需要添您的爱好,在一系列checkbox框中勾选出您喜欢的。
- 需要传入一组数目不确定的域对象。比如在旅游类的电子商务应用中,添加一个旅游团之后,还需要把所有的参团人员的基本信息添入。
等等问题,那么接下来就来深入的讨论一下。
4.3.3 传入非String类型的值
前面的示例,从页面传入Action的值都是String类型的,可是在实际开发中,并不是每次传递的数据都是String类型,也可能需要传递别的类型的值,比如传递int类型,好在Struts2能帮助我们完成从String类型到基本类型的自动转换。
1:传入基本类型的值
假如把Action的account改成int类型的,那么该如何对应呢?注意这里只是用int类型来做个示例,其他基本类型也是一样的做法。
(1)此时Action的示例代码如下:
(2)此时的登录页面很简单,不需要任何特殊的处理,示例代码如下:
(3)重新访问登录页面,记得在账号的文本框里面填写数字,填写后点击提交,看看后台输出的值:
看上去一切很好,Struts2已经正确的帮我们把request中account的字符串转换成int类型了。
(4)但是,如果在登录页面上不填账号,再次运行一下,会发现后台打印了好多好多错误,如下(错误太多,省略了其中的大部分):
- ognl.OgnlException: account [java.lang.IllegalArgumentException: Can not set int
- field cn.javass.action.action.HelloWorldAction.account to java.lang.String]
- at ognl.ObjectPropertyAccessor.setPossibleProperty(ObjectPropertyAccesso
- r.java:103)
- ......省略了
- Caused by: java.lang.IllegalArgumentException: Can not set int field cn.javass.a
- ction.action.HelloWorldAction.account to java.lang.String
- ......省略了
- ... 62 more
- /-- Encapsulated exception ------------\
- java.lang.IllegalArgumentException: Can not set int field cn.javass.action.actio
- n.HelloWorldAction.account to java.lang.String
- ......省略了
- \--------------------------------------/
- the account=0,password=22,submitFlag=login
先看看上面加粗的以“Caused by”开头的那句描述,很明确的表明是在设置int型的account属性时出现错误,因为这次页面没有填写account的值,那么传递过来就是一个空字符串或者是null,但不管是哪种情况,都无法转换成为int类型的值,因此就出错了。
再看看最后一句输出,可以得到结论,虽然对应account的值出错了,但是不影响其他属性的取值,password和submitFlag能正确取到值。
因此,如果属性采用基本类型的时候,如果用户没有填写则会抛错,不过这个错误并不影响其他属性值的对应。
2:使用包装类型
现在,再用Integer来试一试,看看会出现什么情况。
(1)登录页面不需变化,只是把Action中的account属性的类型改为Integer,同时把它变成private的,然后提供相应的getter/setter方法,示例如下:
- public class HelloWorldAction extends ActionSupport{
- private Integer account;
- public String password="";
- public String submitFlag ="";
- public String execute() throws Exception {
- System.out.println("the account="+account+",password="+password+",submitFlag="+submitFlag);
- return "toWelcome";
- }
- public Integer getAccount() {
- return account;
- }
- public void setAccount(Integer account) {
- this.account = account;
- }
- }
(2)重新访问登录页面,在账号的文本框里面填写数字,填写后点击提交,看看后台输出的值,没有任何问题,仍然会正常输出:
(3)接下来,不填写账号,再次运行,看看会怎样呢?
后台运行不再报错,同样能输出值,只是account的值为null而已,如下:
这说明如果使用包装类型的话,就无需关心或者去特别处理Struts2在对应值的时候,自动类型转换所报出的错误了。
(4)可能有些朋友会想,account类型改为Integer后,为什么要为它添加getter/setter方法呢?你可以不去添加getter/setter方法,而是让account为public的,试试看,应该会抛出如下错误:
这个错误的意思是:没有找到一个叫做account的property,注意这里用了property而不是直接翻译成“属性”,是因为这里有一个准确理解的问题。
在日常开发中,可能大家并不去关心“property”的准确含义,一般都是当作属性理解,那么“attribute”呢?也是当作“属性”理解吧,那么他们有什么区别呢?
做过设计的朋友可能会很清楚,“property”和“attribute”是不同的。简单点说,“attribute”是用来描述对象固有的一些属性,一般是创建过后不变的一些值,比如:人这个对象,有手这个“attribute”,正常情况下,创建一个人的实例对象过后,手这个属性一般就不变了。因此“attribute”通常就表现成为私有的属性。
而“property”也是属性,但是一般是创建过后可变的一些值,比如:人这个对象,有一个头发颜色这个“property”,创建对象实例过后,这个人可能去染发了,变成其他颜色了,也就是这个属性的值是可以通过外部来改变的。因此“property” 通常就表现成为私有的属性,并为它设置相应的getter/setter方法。
好了,现在来理解上面那个错误的意思,“没有找到一个叫做account的property”,这就明确告诉我们了,account不是一个“property”,也就是说account这个属性没有相应的getter/setter方法。
4.3.4 如何处理传入多个值
在实际开发中,同一个属性需要传入多个值的情况也是很常见的。下面就来讨论一下,看看到底如何处理这种情况。
1:传入一组数目不确定的字符串
比如在注册用户的时候,可能需要添用户的爱好,也就是在一系列文本框中选出用户喜欢做的事情。页面示例如下:
注意:“habits”在传入action的时候并不知道到底有几个值,可能是一个值,也可能是多个值,如果用户选择了其中两个,就会得到一个有两个字符串的数组。
那么,Action中该如何写才能正确接收这些值呢?
在这种情况下,Action中可以有以下两种写法来对应:
(1)定义一个私有的String数组类型的属性,提供相应的getter/setter方法,示例如下:
当然直接定义一个public的String数组类型的属性也可以,示例如下:
(2)定义一个私有的集合类型的属性,比如List类型的,提供相应的getter/setter方法,示例如下:
当然直接定义一个public的集合类型的属性也可以,示例如下:
(2)这时候在页面上要按照如下的示例来写:
要注意上面的写法,“属性名称[索引]”。上面这样写Struts2就会把上面的4个文本框组成两个UserModel,第1个和第2个一组,第3个和第4个一组。
(3)除了使用private的List及其getter/setter之外,同样还可以使用public的List,示例代码如下:
注意,使用public的List的时候,必须在声明的时候就新建一个ArrayList,否则运行会报“NullPointerException”。
(4)另外一点,如果在页面上忘了写索引,如下:
那么Action接到的将不是两个对象,而是四个,分别拥有一个属性的值。
私塾在线网站原创《研磨struts2》系列
转自请注明出处:【http://sishuok.com/forum/blogPost/list/0/4047.html】
欢迎访问http://sishuok.com获取更多内容
发表评论
-
研磨struts2 目录贴
2012-08-24 12:12 162601.1 《研磨struts2》 第一章 Struts2 ... -
跟着cc学设计 之 研磨设计模式 视频教程 出炉了
2012-08-15 07:16 2研磨设计模式——跟着CC学设计系列精品课程,上线了! ... -
研磨struts2 目录
2012-07-30 21:15 2471.1 《研磨struts2》 第一章 Strut ... -
研磨struts2 目录
2012-07-30 16:54 361.1 《研磨struts2》 第一章 Stru ... -
《研磨struts2》A.2 struts.properties的配置 之 A.2.1 概述
2012-07-09 12:10 2986A.2.1 概述 如果我们希望覆盖在 ... -
《研磨struts2》附录A Struts2的配置 之 A.1 struts.xml的配置
2012-07-09 12:10 2673A.1 struts.xml的配置 A.1.1 ... -
《研磨struts2》第二十一章 零配置 之 21.3 通过注解来实现零配置
2012-07-06 08:07 282421.3 通过注解来实现零配置 21.3.1 ... -
《研磨struts2》第二十一章 零配置 之 21.2 约定大于配置
2012-07-05 10:44 238321.2 约定大于配置 21.2.1 约定 ... -
《研磨struts2》第二十一章 零配置 之 21.1 概述
2012-07-05 10:44 222321.1 概述 21.1.1 零配置概述 在 ... -
《研磨struts2》第二十章 整合SiteMesh 之 20.3 整合Struts2与SiteMesh
2012-07-04 08:15 1997在Struts2中使用SiteMesh ... -
《研磨struts2》第二十章 整合SiteMesh 之 20.2 单独使用SiteMesh
2012-07-03 08:00 199820.2 单独使用SiteMesh 20. ... -
《研磨struts2》第二十章 整合SiteMesh 之 20.1 调整应用的风格
2012-07-03 07:59 179920.1 调整应用的风 ... -
《研磨struts2》19.2 使用execAndWait拦截器模拟进度条 之 19.2.1 模拟长时间运行的Action
2012-07-02 12:24 207719.2.1模拟长时间运行的Action ... -
《研磨struts2》第十九章 进度条 之 19.1 使用“进度条”告知用户进度
2012-07-02 12:23 206919.1 使用“进度条”告知用户进度 ... -
《研磨struts2》第十八章 结合JFreeChart 之 18.3 Struts2结合JFreeChart
2012-06-28 12:12 193518.3 Struts2结合JFreeChart ... -
《研磨struts2》第十八章 结合JFreeChart 之 18.2 Struts2插件概述
2012-06-28 12:11 1962Struts2并没有求大求全,企图把所有 ... -
《研磨struts2》第十八章 结合JFreeChart 之 18.1 JFreeChart使用
2012-06-20 15:48 219318.1 JFreeChart使用 18.1.1概 ... -
研磨struts2(1-10章)电子书下载
2012-06-19 07:30 5974研磨struts2的博客文章 私塾在线学习网 《研 ... -
《研磨struts2》第十七章 防止重复提交 之 17.3 更强大的tokenSession拦截器
2012-06-18 13:46 201417.3 更强大的tokenSession拦截器 ... -
《研磨struts2》第十七章 防止重复提交 之 17.2 使用标签
2012-06-18 13:45 229217.2 使用<s:token/>标签 ...
相关推荐
研磨Struts2
《研磨Struts2》原书配套源代码,配合Struts2原书使用,快速学习Struts2技术。
由于文件较大,我把这个文档切割为2部分,这是第一部分,请下载完第一部分后一定要下载第二部分,否则不能阅读。
研磨struts2博文,详细讲解了struts2运行流程,值得大家阅读
《研磨Struts2》原书配套PPT,结合《研磨Struts2》配套使用,快速学习Struts2框架。
研磨struts2_系列文章.pdf
研磨Struts2 高清完整版,请和第一部分一起下载啊
《研磨Struts 2》PDF版本下载
研磨Struts2_12859679_高清完整版
研磨Struts2-高清-完整目录-2011年10月,分享给所有需要的人
研磨struts,仅是简版,我也是从网上下来的。大家凑合看吧。
第4章 适配器模式(Adapter) 第5章 单例模式(Singleton) 第6章 工厂方法模式(Factory Method) 第7章 抽象工厂模式(Abstract Factory) 第8章 生成器模式(Builder) 第9章 原型模式(Prototype) 第10章...
第4章 适配器模式(Adapter) 第5章 单例模式(Singleton) 第6章 工厂方法模式(Factory Method) 第7章 抽象工厂模式(Abstract Factory) 第8章 生成器模式(Builder) 第9章 原型模式(Prototype) 第10章...
Java工程师三大框架面试题.pdf
第4章 适配器模式(Adapter) 第5章 单例模式(Singleton) 第6章 工厂方法模式(Factory Method) 第7章 抽象工厂模式(Abstract Factory) 第8章 生成器模式(Builder) 第9章 原型模式(Prototype) ...
研磨设计模式.part2 一定要下载5部分