首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

《研磨struts2》第四章 Action 之 4.2 Action的兑现

2012-06-28 
《研磨struts2》第四章 Action 之 4.2 Action的实现4.2? Action的实现4.2.1? POJO的实现在Struts2中,Action

《研磨struts2》第四章 Action 之 4.2 Action的实现

4.2? Action的实现

4.2.1? POJO的实现

在Struts2中,Action可以不实现任何特殊的接口或者继承特殊的类,仅仅是一个POJO(Plain Old Java Object,简单的Java对象)就可以,但是要有一个公共的为空参的构造方法,其实缺省的构造方法就可以,还要有一个execute方法,定义格式如下:

?

java代码:查看复制到剪贴板打印
  1. public?String?execute()?throws?Exception{??
  2. ????…??
  3. }??

这个execute方法要求:

  • 可见性为public
  • 不需要传入参数
  • 返回一个字符串,其实就是指示的下一个页面的result。
  • 可以抛出Exception,当然也可以不抛例外

    也就是说,任意写一个满足上述要求的POJO都可以算作是Struts2的Action实现。但是,在实际开发的时候,通常会让Action实现Action接口或继承ActionSupport类。

    4.2.2??实现Action接口

    就像在HelloWorld中的示例中一样,可以让我们自己写的Action类实现Struts2的Action接口。如:

    ?

    java代码:查看复制到剪贴板打印
    1. import?com.opensymphony.xwork2.Action;??
    2. public?class?HelloWorldAction?implements?Action?{??
    3. ????//省略了??
    4. }??

    注意:在导入Action接口的时候,有好多同名不同包的类都叫Action,比如javax.swing.Action,一定要注意,我们需要使用Xwork2中的Action接口。

    ?????? 在Action接口中也仅仅定义了前面说的execute方法,除此之外,还有一系列预定义的字符串常量,这些常量可以用于返回一些预定的result,比如:

    ?

    java代码:查看复制到剪贴板打印
    1. public?static?final?java.lang.String?SUCCESS?=?"success";??
    2. ??public?static?final?java.lang.String?NONE?=?"none";??
    3. ??public?static?final?java.lang.String?ERROR?=?"error";??
    4. ??public?static?final?java.lang.String?INPUT?=?"input";??
    5. ??public?static?final?java.lang.String?LOGIN?=?"login";??

    4.2.3??继承ActionSupport类

    由于Xwork的Action接口非常简单,为程序员提供的帮助有限,因此,在实际开发中,会更多的使用继承ActionSupport类来实现Action的方式,示例代码如下:

    ?

    java代码:查看复制到剪贴板打印
    1. import?com.opensymphony.xwork2.ActionSupport;??
    2. public?class?HelloWorldAction?extends?ActionSupport?{??
    3. ????//省略了??
    4. }??

    ActionSupport类本身实现了Action接口,所以继承ActionSupport类就相当于实现了Action接口。???? 除此之外,ActionSupport类还实现了其它几个接口,来为程序员提供更多使用的功能,比如:

    • com.opensymphony.xwork2.Validateable:提供validate()方法来为Action增加验证的功能
    • com.opensymphony.xwork2.Validateaware:提供方法来保存和恢复action或field级的错误信息
    • com.opensymphony.xwork2.TextProvider:提供获取本地信息文本的功能
    • com.opensymphony.xwork2.LocaleProvider:提供getLocale()方法来获取本地消息

      ?????? 这些接口和Struts2的一些其他特性相结合,可以实现基本的数据验证功能和国际化。更多关于数据验证和国际化的知识,以后有专门的章节去讲解。

      ?????? 接下来简要的示范一下,当Action继承ActionSupport类过后,如何实现数据验证和访问本地信息。

      1:示例基本的数据验证

      (1)要实现数据验证的功能,只需要在Action类中覆盖实现validate方法即可;在validate方法内部,对请求传递过来的数据进行校验,如果不满足要求,那么添加例外信息到父类用于存放例外的集合中。示例代码如下:

      ?

      java代码:查看复制到剪贴板打印
      1. package?cn.javass.action.action;??
      2. import?com.opensymphony.xwork2.ActionSupport;??
      3. public?class?HelloWorldAction?extends?ActionSupport?{??
      4. ????private?String?account;??
      5. ????private?String?password;??
      6. ????private?String?submitFlag;??
      7. ????public?String?execute()?throws?Exception?{??
      8. ????????this.businessExecute();??
      9. ????????return?"toWelcome";??
      10. ????}??
      11. ????public?void?validate(){??
      12. ????????if(account==null?||?account.trim().length()==0){??
      13. ????????????this.addFieldError("account",?"帐号不允许为空");??
      14. ????????}??
      15. ????????if(password==null?||?password.trim().length()==0){??
      16. ????????????this.addFieldError("password",?"密码不允许为空");??
      17. ????????}??
      18. ????????if(password==null?||?password.trim().length()<6){??
      19. ????????????this.addFieldError("password",?"密码长度必须在6位以上");??
      20. ????????}??
      21. ????}??
      22. ????/**?
      23. ?????*?示例方法,表示可以执行业务逻辑处理的方法,?
      24. ?????*/??
      25. ????public?void?businessExecute(){??
      26. ????????System.out.println("用户输入的参数为==="+"account="+account??
      27. ??????????????????+",password="+password+",submitFlag="+submitFlag);??
      28. ????}??
      29. ????//属性对应的getter/setter,为节省篇幅,省略了??
      30. }??

      从上面的示例可以看出,在validate方法中,可以对用户请求中传递过来的数据进行验证,同一个数据可以进行多方面的验证。

      如果验证结果是数据不正确,那么就使用父类提供的addFieldError方法来添加验证的错误消息。addFieldError方法有两个参数,前面的是消息的key值,后面是具体的消息。

      (2)细心的你肯定发现了,validate方法是没有返回值的,那么当验证后,如果有数据没有通过验证,该返回到什么页面呢?

      这就需要在struts.xml中的Action配置里面,添加一个名称为input的result配置,也就是说,如果validate方法中,有数据没有通过验证,那么会自动跳转回到该action中名称为input的result所配置的页面。示例如下:

      ?

      java代码:查看复制到剪贴板打印
      1. <?xml?version="1.0"?encoding="UTF-8"??>??
      2. <!DOCTYPE?struts?PUBLIC??
      3. ????"-//Apache?Software?Foundation//DTD?Struts?Configuration?2.0//EN"??
      4. ????"http://struts.apache.org/dtds/struts-2.0.dtd">??
      5. ??
      6. <struts>??
      7. ????<constant?name="struts.devMode"?value="true"?/>??
      8. ????<constant?name="struts.locale"?value="zh_CN"/>??
      9. ????<constant?name="struts.i18n.encoding"?value="gb2312"/>??
      10. ??
      11. ????<package?name="helloworld"??extends="struts-default">??
      12. ????????<action?name="helloworldAction"?class="cn.javass.action.action.HelloWorldAction">??
      13. ????????????<result?name="toWelcome">/s2impl/welcome.jsp</result>??
      14. ????????????<result?name="input">/s2impl/login.jsp</result>??
      15. ????????</action>??
      16. </package>??
      17. </struts>??

      (3)测试一下,回到登录页面,不输入帐号和密码,点击提交,你会发现,页面好像没有动,还是登录页面。

      ?????? 其实,登录页面已经提交了,到了HelloWorldAction中,会先运行validate方法,当validate方法运行结束后,调用Action的运行环境会去检查存放验证消息的集合,如果这个集合有值,就表示有数据没有通过验证,它就会直接跳转页面到名称为input的result所配置的页面,也就是登录页面。

      ??? 因此从表面上看,好像页面没有动,其实不是这样的。为了验证是不是经过了validate,你可以在validate方法里面输出一句话,然后再运行一下就可以看出是否运行过了。

      可能有朋友会说,那为何不直接在页面输出在validate方法中配置的错误信息呢?主要是通常会采用Struts2的标签来获取并展示这些错误信息,这里不去深入标签的细节,简单示范一下,把错误消息显示出来就可以了:

      • 修改登录页面,添加对Struts2标签的支持
      • 添加显示消息的代码,用标签实现

        新的登录页面如下:

        ?

        java代码:查看复制到剪贴板打印
        1. <%@?page?language="java"?contentType="text/html;?charset=gb2312"??
        2. ????pageEncoding="gb2312"%>??
        3. <html>??
        4. <head>??
        5. <meta?http-equiv="Content-Type"?content="text/html;?charset=gb2312">??
        6. <title>Insert?title?here</title>??
        7. </head>??
        8. <body>??
        9. <%@?taglib?prefix="s"?uri="/struts-tags"%>??
        10. <s:if?test="hasFieldErrors()">??
        11. ????<s:iterator?value="fieldErrors">??
        12. ????????<font?color=red><s:property?value="value[0]"/></font><br>??
        13. ????</s:iterator>??
        14. </s:if>??
        15. ??
        16. <form?action="/helloworld/helloworldAction.action"?method="post">??
        17. ????<input?type="hidden"?name="submitFlag"?value="login"/>??
        18. ????账号:<input?type="text"?name="account"><br>??
        19. ????密码:<input?type="password"?name="password"><br>??
        20. ????<input?type="submit"?value="提交">??
        21. </form>??
        22. ??
        23. </body>??
        24. </html>??

        再次运行一下,不填写帐号和密码,点击提交,会得到如下的界面

        《研磨struts2》第四章 Action 之 4.2 Action的兑现

        图4.1 带错误提示的登录页面

        ?????? 当然,你可以在帐号和密码中输入数据,别忘了,密码数据要在6位以上,validate方法里面有检测的,再次点击提交,哈哈,正常转向欢迎页面了吧。

        ?????? (4)通过这个示例,你会发现,validate方法会先于execute方法被执行,只有validate方法执行后,又没有发现验证错误的时候,才会运行execute方法,否则会自动跳转到你所配置的input所对应的页面。

        2:示例访问本地信息

        ?????? 在上面的示例中,你会发现在validate方法中,添加验证错误消息的时候,是采用的硬编码方式,也就是直接写死的字符串,这是很不好的:

        • 一是不容易修改,比如要改变消息的内容,还得重新修改和编译类
        • 二是不利于国际化,如果要把中文的信息变换成英文的呢,同样要重新修改和编译类

          可以通过访问本地信息的方式,把这些错误消息放置到Action类外部的配置文件中,在Action类内部只需要按照这些消息的key值去获取消息即可。这样一来,当消息发生变化的时候,只需要修改这个消息的配置文件即可。

          (1)先来建立消息的配置文件,在Action类的路径下建立一个同名的properties文件,也就是文件名为HelloWorldAction.properties,然后在里面按照key=value的格式,添加要使用的错误消息。示例如下:

          ?

          java代码:查看复制到剪贴板打印
          1. k1=\u5E10\u53F7\u4E0D\u5141\u8BB8\u4E3A\u7A7A??
          2. k2=\u5BC6\u7801\u4E0D\u5141\u8BB8\u4E3A\u7A7A??
          3. k3=\u5BC6\u7801\u957F\u5EA6\u5FC5\u987B\u57286\u4F4D\u4EE5\u4E0A??

          可能你会觉得很奇怪,这都是些什么呀。其实是把中文的消息转换成了相应的unicode编码,比如k1后面的value值,其实就是“帐号不允许为空”的unicode编码。只有这样,在程序里面读取到这些值的时候才会正确显示中文。

          有很多工具可以把中文转换成unicode编码,JDK就提供了一个native2ascii的命令来实现这样的功能,具体的可以参见相应的文档,在国际化一章也会讲述native2ascii的基本使用。

          (2)Action里面,就修改validate方法,原来是直接写的中文字符串,现在应该修改成从配置文件中获取信息了,示例如下:

          ?

          java代码:查看复制到剪贴板打印
          1. ???public?void?validate(){??
          2. ????if(account==null?||?account.trim().length()==0){??
          3. ????????this.addFieldError("account",?this.getText("k1"));??
          4. ????}??
          5. ????if(password==null?||?password.trim().length()==0){??
          6. ????????this.addFieldError("password",??this.getText("k2"));??
          7. ????}??
          8. ????if(password==null?||?password.trim().length()<6){??
          9. ????????this.addFieldError("password",??this.getText("k3"));??
          10. ????}??
          11. }??

          这里的“k1”、“k2”、“k3”就对应前面配置文件的key值。

          再测试一下,看看效果。

          4.2.4? execute方法内部实现方式

          看到这个地方,知道了Action类的基本写法,可以是POJO,也可以实现Action接口,当然更推荐继承ActionSupport类。可是这样就够了吗?会写Action了吗?

          ?????? 当然不是,那还需要学什么呢?

          ?????? 回忆一下Action类的这三种写法,会发现都要实现一个execute的方法,这个方法就是用来处理用户请求的方法,因此,一定要学会execute方法内部如何实现,才算基本掌握了Action的写法,才能写出完整的Action实现来。

          ?????? 在实际开发中,execute方法内部通常需要实现如下工作:

          • 收集用户传递过来的数据
          • 把收集到的数据组织成为逻辑层需要的类型和格式
          • 调用逻辑层接口,来执行业务逻辑处理
          • 准备下一个页面所需要展示的数据,存放在相应的地方
          • 转向下一个页面

            结合着HelloWorldAction来看看execute方法的实现,示例代码如下:

            ?

            java代码:查看复制到剪贴板打印
            1. public?class?HelloWorldAction?extends?ActionSupport?{??
            2. ????private?String?account;??
            3. ????private?String?password;??
            4. ????private?String?submitFlag;??
            5. ??????
            6. ????public?String?execute()?throws?Exception?{??
            7. ????????//收集参数??
            8. ????????//组织参数??
            9. ????????//调用逻辑层来进行逻辑处理??
            10. ????????this.businessExecute();??
            11. ????????//准备下一个页面所需要的数据??
            12. ????????//转向下一个页面??
            13. ????????return?"toWelcome";??
            14. ????}??
            15. ????/**?
            16. ?????*?示例方法,表示可以执行业务逻辑处理的方法,?
            17. ?????*/??
            18. ????public?void?businessExecute(){??
            19. ????????System.out.println("用户输入的参数为==="+"account="??
            20. ??????????+account+",password="+password+",submitFlag="+submitFlag);??
            21. ????}??
            22. ????//属性对应的getter/setter方法,省略了??
            23. }??

            详细来看看上例中execute方法的实现方式:

            1:收集用户传递过来的数据

            也就是接收提交请求的页面传过来的参数。在这里不需要和HttpServletRequest打交道,只要页面元素的名称和Action的属性有对应关系,这个对应由OGNL(对象图导航语言,后面详细讲述)来表达,那么在execute方法运行之前,页面的值会自动对应到这些属性上来,这个功能是通过拦截器(后面会详细讲述)来实现的。

            简而言之,在上述例子中,在execute方法里面,不需要用代码来实现收集参数的功能,页面传递过来的数据会自动填充到Action的属性中。

            2:组织参数

            ?????? 由于这里并没有真正去调用逻辑层,因此也不知道究竟需要什么类型的数据,也不知道具体是什么格式。

            ?????? 因此在上面的例子中,没有代码来实现组织参数。如果要写代码呢,会是什么样呢?

            ?????? 假如逻辑层需要传入一个封装好数据的对象,比如HelloWorldModel吧,那么这里的写法大致如下示例:

            ?

            java代码:查看复制到剪贴板打印
            1. public?String?execute()?throws?Exception?{??
            2. ????//收集参数??
            3. ????//组织参数??
            4. ???????HelloWorldModel?hwm?=?new?HelloWorldModel();??
            5. ???????hwm.setAccount(account);??
            6. ???????hwm.setPassword(password);??
            7. }??

            很简单,对吧,就是把数据组织成为逻辑层需要的类型和格式就可以了。

            3:调用逻辑层来进行逻辑处理

            在上述示例中,并没有真正调用逻辑层,只是简单的调用了一个内部示例逻辑处理的方法,那么在实际的应用中,应该怎么写呢?

            假如有一个逻辑层的接口,设若名称为LoginEbi,里面包含有业务逻辑处理方法,那么在execute方法里面,大致写法如下示例:

            ?

            java代码:查看复制到剪贴板打印
            1. public?String?execute()?throws?Exception?{??
            2. ????//收集参数??
            3. ????//组织参数??
            4. ???????HelloWorldModel?hwm?=?new?HelloWorldModel();??
            5. ???????hwm.setAccount(account);??
            6. ???????hwm.setPassword(password);??
            7. ????//调用逻辑层来进行逻辑处理??
            8. ????//如何得到LoginEbi对象,这里就不去深究了??
            9. ????loginEbi.businessExecute(hwm);??
            10. }??

            4:准备下一个页面所需要的展示数据

            在上述示例中,并没有去为欢迎页面准备数据,因为欢迎页面只需要显示帐号,而帐号的数据就存放在Action的属性account中。

            如果在实际的应用中,要为下一个页面准备数据的话,很可能需要调用逻辑层,通过逻辑层来获取需要的数据,这里就不去示例了。

            5:转向下一个页面

            在execute方法执行最后,会返回一个result,其实就是个字符串,代表选择下一个页面跳转到哪里。这个字符串对应哪一个页面,配置在struts.xml中这个Action的result子元素里面,这个字符串就是<result>元素的name属性,如果匹配上了,则会跳转到那个result配置所指定的页面。

            4.2.5??简单的单元测试

            通过上面的讲解,你会发现,无论是使用POJO、还是实现Action接口、还是继承ActionSupport类来实现Action,都与Servelt的API不相关,因而可以很容易的使用java application来对Action类进行单元测试。

            ?????? 比如在HelloWorldAction中写一个main方法,就可以按照Java应用的方式来运行了。

            ?

            java代码:查看复制到剪贴板打印
            1. public?static?void?main(String[]?args)?{??
            2. ????try{??
            3. ????????HelloWorldAction?action?=?new?HelloWorldAction();??
            4. ????????action.setAccount("test");??
            5. ????????action.setPassword("test");??
            6. ??????????
            7. ????????action.execute();??
            8. ????}catch(Exception?err){??
            9. ????????err.printStackTrace();??
            10. ????}??
            11. }??

            由于HelloWorldAction没有与Servlet API耦合,因而可以直接新建这个类的实例;调用它的setter方法,相当于传入请求的参数;调用execute方法,就开始处理请求了。

            ?????? 这样一来,在实际开发中,就可以在不启动web服务器的情况下,直接模拟request传入参数,并处理请求,从而测试Action是否能正确处理请求,这在单元测试时候十分有用。

            当然,对于Action的单元测试,后面会有专门的章节介绍,这里只是简要的讲述一下,顺便让大家体会Action与Servlet API解耦,以及解耦后带来的好处,当然能简单的进行单元测试只是其好处之一。

            ?

            私塾在线网站原创《研磨struts2》系列

            转自请注明出处:【http://sishuok.com/forum/blogPost/list/0/4046.html】

            欢迎访问http://sishuok.com获取更多内容

热点排行