zoukankan      html  css  js  c++  java
  • Struts 应用转移到 Struts 2 二

    Struts 应用转移到 Struts 2 二

    向 Struts2 迁移
         在Struts2中,可选的实现方式有很多,可以像Struts那样每个需求用例对应一个action,也可以用一个action对应所有需求用例。但在我们的例子中,使用的方法是我认为最佳的解决方案 - 在一个action类中实现整套CRUD功能。
         也许你人为把list需求用例也同样地整合到同一个action类里会比较好,而我认为把list的功能分到另外一个action中,会减少容易产生的混淆,因为list用例中并不需要Blog这个类作为属性,而在其他用例中则需要。

    对于 Struts2的例子, 它的UML模型展示如下:


         每个用例在action中都有自己所对应的方法。从上图中我们可以看到,在BlogAction 中我们有save, update 和 remove三个方法。而ListBlogAction中,没有list这个方法,因为ListBlogAction继承了ActionSupport 类,实际上就是在默认的execute 方法中实现list功能。
         为了更容易看,图中的BlogAction并没有画出它所实现了的三个接口。它们分别是ServletRequestAware 接口,  Prepareable 接口和 ModelDriven 接口。
         首先回顾一下ServletRequestAware, 我们在第一篇文章中已经详细介绍它了。这个ParametersInterceptor 拦截器提供了把HttpServletRequest 自动set到action中的功能,让我们能通过request, 把所需的值传回到JSPs。
         接着看看Preparable 接口, 它会联合PrepareInterceptor拦截器一起工作,让action在执行execute() 方法前, 执行一个prepare()方法,实现在执行前设定,配置或预设一些值在action中。 在我们的例子里,prepare方法会检查blogId 属性,如果为零则这是一个新日志,非零则该日志已经存在,根据blogId取出日志。
         最后我们说说ModelDriven 接口,在上一篇文章中,我们已经了解到 Struts action的很大的不同在于它是需要线程安全的,而在Struts2中则没有这个限制,因为每次的请求都会有一次action对象的初始化和调用。没有了这个限制,能允许Struts2使用类级别的属性变量(特别是getters和setters),从而获得更多编码优势。

    和拦截器的功能结合起来, 把HttpServletRequest 中的attribute 注入action中的流程如下所示:

    • 循环读取HTTP request中的attribute
    • 查找当前request attribute中是否有和action中的setter中的属性匹配的
    • 有则根据attribute从HttpServletRequest 里取出其值
    • 把取出来的值由String转成setter中相应的类型
    • 调用setter把该转换后的值注入action中
    提示: 当调用action时,如果发现不明原因使不能正确地通过setter注入值情况下,第一步最好是先检查下各拦截器,确保它们都已作用于该action。因为这些意外通常有时由拦截器设置不当形成的,检查是否各个拦截器都已起作用,并看看它们作用的顺序,因为有些情况下它们间会相互影响而产生错误。

        现在我们已经有基于String类型的form bean中取值的方法或者是自动把request的attributes 注入到action的方法,那下一步就是如何把值传入 domain object 或 value / transfer object的属性中去。其实这很简单,你只需要实现ModelDriven 接口(即实现getModel()方法)就可以了,确保ModelDrivenInterceptor 拦截器已作用于action。
        除了会调用action中的setter外,model 首先检查是否有和setter可以匹配当前的attribute名。如果在model中没有这个attribute相应的setter,则会再在action上找相应的setter来设值。
        在BlogAction 的例子中我们可以看到如何很灵活地使用这些方法,首先通过prepare() 方法根据Id获取相应的 Blog model object 或新建一个instance, 然后再根据把request中相应的属性注入Blog instance中和action中。
        以上的两个功能使得现在调用action那么简单 - 调用具体的业务逻辑,和把数据设在HttpServletRequest供返回用


    public class BlogAction extends ActionSupport
             
    implements ModelDriven, Preparable, ServletRequestAware 

         
    private int blogId;
         
    private Blog blog;
         
    private BlogService service = new BlogService();
         
    private HttpServletRequest request;

         
    public void setServletRequest(HttpServletRequest httpServletRequest){ 
             
    this.request = httpServletRequest;
         }


          
    public void setId(int blogId) {
             
    this.blogId = blogId;
         }


          
    public void prepare() throws Exception 
             
    if( blogId==0 ) 
                 blog 
    = new Blog();
             }
     else 
                 blog 
    = service.findById(blogId);
             }

         }


          
    public Object getModel() 
             
    return blog;
         }


          
    public String save() 
             service.create(blog);
             
    return SUCCESS;
         }

          
    public String update() 
             service.update(blog);
             request.setAttribute(
    "blog",blog);
             
    return SUCCESS;
         }


          
    public String remove() 
             service.delete(blogId);
             
    return SUCCESS;
         }


          
    public String execute() 
             request.setAttribute(
    "blog",blog);
             
    return SUCCESS;
         }



    }
     


    最后就是说说 list这个用例了。它同样需要访问HttpServletRequest对象去返回数据给JSP,所以也需要实现ServletRequestAware 接口。但是,因为它并不需要任何输入值,所以就不需要实现其他的接口了。以下是它的具体实现:

    public class ListBlogsAction extends ActionSupport implements ServletRequestAware 

         
    private BlogService service = new BlogService();
         
    private HttpServletRequest request;

         
    public void setServletRequest(HttpServletRequest httpServletRequest) 
             
    this.request = httpServletRequest;
         }


         
    public String execute() 
             request.setAttribute(
    "bloglist",service.list());
             
    return SUCCESS;
         }


    }


    这样就完成了我们该实现的action代码了。 在下一篇文章中,当我们新的Struts2用户界面结合时,我们还会进一步简化action的代码。


    配置Actions
        在我们调用action之前,我们必须通过XML配置文件去配置它们。
        在Struts中, 我们习惯用在WEB-INF 目录的"struts-config.xml"配置文件,在这里我们需要配置action form和action属性。在Struts2中, 用的是在classpath中的"struts.xml"配置文件, 它看起来好象会稍微复杂一些,因为它需要在配置action的同时也配置其拦截器。

        在Struts中配置 form-beans 节点很容易, 只需要一个唯一的名字,还有就是继承ActionForm类的class作为type。

    <struts-config>

         
    <form-beans>
             
    <form-bean name="blogForm"
        type
    ="com.fdar.articles.infoq.conversion.struts.BlogForm"/>
         
    </form-beans>
         ...

    </struts-config> 


    在我们的例子中,我们的配置文件有3点不相同:
    1. 重定向配置
        在Struts的配置中,每个mapping 都需要提供调用action时所需要对应的路径,Struts默认为".do", 例如paht是"/struts/add"对应于URL"/struts/add.do"。同时也需要一个forward 属性来提供给URL去转向,如"/struts/add.jsp".

    <struts-config>
         ...

          
    <action-mappings>

              
    <action path="/struts/add" forward="/struts/add.jsp"/>
             ...

          
    </action-mappings>
    </struts-config> 


    而Struts2的需要更多的一些配置,如:

    <struts>
          
    <include file="struts-default.xml"/>

          
    <package name="struts2" extends="struts-default" namespace="/struts2">

              
    <action name="add" >
                 
    <result>/struts2/add.jsp</result>
             
    </action>
             ...

          
    </package>
    </struts> 


    首先你会注意到的是,代替action-mappings 节点的是includepackage 节点。Struts2可以把配置细分到任意数目的配置文件中,来实现配置可模块化管理。每个配置文件的结构其实都是一样的,不同的只是文件名。
        include 节点中,以文件名作为file 属性,可把所include的文件内容包含到当前文件中。
       package 节点把actions组成一组,其name 属性的值必须是唯一的。
       在 Struts action的配置中, paht属性需要指定完整的URL路径。而在Struts2中,URL是通过package节点中的namespace属性,还有在action 节点中的name 属性, 和action扩展(默认是".action")共同起作用的。在上面的例子中,则URL为"/struts2/add.action"时会调用action。
       package节点除了可以分离命名空间外, package 节点中的 extends 属性,还提供了某种可复合的组成结构。通过继承另外一个package节点,你就能继承那个节点的配置,包括其actions, results, interceptors, exception,等值。在我们的例子中,"struts2" package节点继承了 "struts-default" package 节点(在"struts-default.xml" 文件里定义了该节点) ,注意这个是主要的include文件,所以必须在所有配置之前的第一行中写出。 这个功能有助于大大减少你重复性输入默认配置所浪费的时间。
        最后是result 节点, 它只是存放你这个action所需要转向的URL. 在这里我们没有提及nametype 属性。如果你不想改变它们的默认属性的话,你能忽略不写它们,让你的配置文件看起来更清晰。从action返回的 "success" 的结果将组成这个JSP显示给用户。


    2. Action 配置
        在Struts 中forward 节点指定了action处理后,结果将重定向到哪个相应的页面。type属性指定了action的类,scope 属性保证了form beans只在request范围内。

    <struts-config>
         ...

          
    <action-mappings>

              
    <action path="/struts/list" scope="request"
                     type
    ="com.fdar.articles.infoq.conversion.struts.ListBlogsAction" >
                 
    <forward name="success" path="/struts/list.jsp"/>
             
    </action>
             ...

          
    </action-mappings>
    </struts-config>


    Struts2 的 XML配置和上面提到的基本相同。唯一不同的就是通过class属性为action节点提供了它所需要调用的类的完整路径

    <struts>
         ...

          
    <package name="struts2" extends="struts-default" namespace="/struts2">

              
    <default-interceptor-ref name="defaultStack"/>

              
    <action name="list"
                     class
    ="com.fdar.articles.infoq.conversion.struts2.ListBlogsAction">
                 
    <result>/struts2/list.jsp</result>
                 
    <interceptor-ref name="basicStack"/>
             
    </action>
             ...

          
    </package>
    </struts>


    如果是用其他的方法而不是用默认的execute 方法去调用action(在BlogAction 类中大多数方法如此), 则需要在action节点的 method 属性里加入方法名,下面就是个例子,这时候update方法将会被调用。

    <action name="update" method="update"
        class
    ="com.fdar.articles.infoq.conversion.struts2.BlogAction" >
            ...
        
    </action> 


    default-interceptor-refinterceptor-ref 节点有几点不同。在第一篇文章中,我们看到在action被调用之前必须通过一系列的拦截器,而这两个节点就是用来配置拦截器组的。default-interceptor-ref 节点为该package提供了默认的拦截器组。当在action节点中提供 interceptor-ref节点时 ,它就会覆盖默认的拦截器(interceptor-ref 节点能够和单独一个拦截器相关联,或者跟一个拦截器组相关联),在action节点中可以存在多个interceptor-ref节点,处理拦截器组的顺序会和该节点列出的顺序一致。


    3. 再重定向配置
        当我们提交表格的时候,我们需要重定向到更新后的结果页面。这个通常称为 "post-redirect pattern" 或, 最近出现的, "flash scope."
        由于这是一个form, 所以在Struts中我们需要为Struts指定一个ActionForm。需要在name属性中提供form的名称,同样地,我们也需要在forward 节点中举加入redirect属性为true。

    <struts-config>
         ...

          
    <action-mappings>
              
    <action path="/struts/save"
                     type
    ="com.fdar.articles.infoq.conversion.struts.SaveBlogEntryAction"
                     name
    ="blogForm" scope="request">
                 
    <forward name="success" redirect="true" path="/struts/list.do"/>
              
    </action>
             ...

          
    </action-mappings>
    </struts-config> 


    Struts2 在result 节点里提供了type 属性, 默认情况下是"dispatch", 如果需要重定向,则需要设为 "redirect"。

    <struts>
         ...

          <package name
    ="struts2" extends="struts-default" namespace="/struts2">

              <action name
    ="save" method="save"
                     class
    ="com.fdar.articles.infoq.conversion.struts2.BlogAction" >
                 <result type
    ="redirect">list.action</result>
                 <interceptor-ref name
    ="defaultStack"/>
              </action>
             ...

          </package>
    </struts> 

    总结
        我们并不可能在这篇文章中覆盖所有的内容,如果你需要更好的了解整个框架,还有其他的实现方式和选项,这里有几点可以供你参考:

    • 配置拦截器和拦截器组 - 以Struts2-core JAR 包里的"struts-default.xml" 文件作为例子。"struts-default.xml" 演示了如何配置你自己的拦截器组,包含新的拦截器,你可以尝试实现自己的拦截器。
    • 配置文件中的通配符模式 - 你可以选择使用Struts2中的通配符模式来简化你的配置。
    • 通过 ParameterAware 接口把form值传入maps中 - 你可以在Struct2中配置,让所有request的form属性都存于action的一个map中,这样就不需要专门再为action指定model / transfer / value object了。这和Struts的dynamic form特点很相似。

        也许到现在为,也许你有个疑问,"迁移后我们的界面是否可以完全重用呢?",答案是yes。你能从这里, 下载到我这篇文章中的完整源代码,你可以自己尝试把URL的扩展名由".do" 改为 ".action",使用的页面时一样的。除此之外,其实用JSTL来代替Struts taglib也是很容易的。

     
  • 相关阅读:
    Java实现 蓝桥杯VIP 算法训练 数的统计
    Java实现 蓝桥杯VIP 算法训练 和为T
    Java实现 蓝桥杯VIP 算法训练 友好数
    Java实现 蓝桥杯VIP 算法训练 连续正整数的和
    Java实现 蓝桥杯VIP 算法训练 寂寞的数
    Java实现 蓝桥杯VIP 算法训练 学做菜
    Java实现 蓝桥杯VIP 算法训练 暗恋
    Java实现 蓝桥杯VIP 算法训练 暗恋
    测试鼠标是否在窗口内,以及测试鼠标是否在窗口停留
    RichEdit 各个版本介绍
  • 原文地址:https://www.cnblogs.com/encounter/p/2189240.html
Copyright © 2011-2022 走看看