向 Struts2 迁移
在Struts2中,可选的实现方式有很多,可以像Struts那样每个需求用例对应一个action,也可以用一个action对应所有需求用例。但在我们的例子中,使用的方法是我认为非常好的的解决方案 - 在一个action类中实现整套CRUD功能。
也许你人为把list需求用例也同样地整合到同一个action类里会比较好,而我认为把list的功能分到另外一个action中,会减少容易产生的混淆,因为list用例中并不需要Blog这个类作为属性,而在其他用例中则需要。
对于 Struts2的例子, 它的UML模型展示如下:
下图image2.jpg
每个用例在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中
QUOTE:
提示: 当调用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供返回用。
现在我们已经有基于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供返回用。
最后就是说说 list这个用例了。它同样需要访问HttpServletRequest对象去返回数据给JSP,所以也需要实现ServletRequestAware 接口。但是,因为它并不需要任何输入值,所以就不需要实现其他的接口了。以下是它的具体实现: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; } }
这样就完成了我们该实现的action代码了。 在下一篇文章中,当我们新的Struts2用户界面结合时,我们还会进一步简化action的代码。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; } }