把一个模型驱动的 Action和“preparable”拦截器/接口,Action配置中的通配符,还有验证与工作流结合以后,你就可以把 CRUD操作放到一个 Action里面去。这种方式与Restful2ActionMapper 的功能很相似。
我们使用的 URL模式为“/{model}/{method}.action”。例如,调用 UserAction类中的“add”方法,那么 URL就是“/User/add.action”。我们同样还需要确保有对应的结果映射——“success”(缺省情况),“input”(验证出现问题的情况)和“home”或是一个默认页面。每一个页面都是模型所特有的。“success”会按照 redirect-after-post模式,重定向到 Action,
下面是相应的“struts.xml”文件:
<action name=”*/*” method=”{2}” class=”com.infoq.actions.{1}Action”> <result type=”redirect”>/{1}/view.action</result> <result name=”view”>/{1}/view.jsp</result> <result name=”input”>/{1}/edit.jsp</result> <result name=”home”>/{1}/home.jsp</result> </action>
这个 Action需要继承 ActionSupport类(提供了验证和错误消息处理的实现),并实现 ModelDriven和 Preparable接口。与这两个接提高效率技巧口协同工作的拦截器栈可以帮助我们简化对接口的实现,下面来看一下具体细节。
ModelDriven接口中有一个方法,getModel(),它与“model-driven”拦截器相结合,把 Action中的模型放到值栈中,位于 Action之上。当请求参数被传入时,它们将会被赋给模型而不是 Action。这就是我们想要看到的——对模型赋值而非 Action——然后我们只需要更新 Action。但是如果模型里面已经有了数值,而我们并不想重写它们呢?
这时就需要“paramsPrepareParamsStack”拦截器栈发挥作用了。下面的步骤就是我们想要做,而栈中的拦截器帮我们做了的工作:
2. Action执行某些逻辑,或是创建一个新模型,或是从
Service上,从数据库中获取一个已有的模型——
“prepare” 拦截器调用 Preparable接口中的 prepare()
方法
3. 当模型已经存在后,把请求参数赋值给模型——先是
“model-driven”拦截器,然后又是“params”拦截器。
4. 查看模型是否存在验证错误,在必要的情况下重定向回
input页面—— “validation”和“workflow”拦截器
5. 执行被调用的方法中的逻辑——普通的 action处理
通过遵守这些约定,你的 Web应用中所有的模型或者实体对象都可以通过上面的“struts.xml”文件来管理。只是 Action的实现需要根据情况不同而发生变化。在我们的例子中,UserAction的代码为:
public class UserAction
extends ActionSupport
implements ModelDriven, Preparable{
private User user;
private int id;
private UserService service; // user 业务服务
…
public void setId(int id) {
this.id = id;
/** 如果user 不存在的话,就创建一个新的,否则就根据特定
的id 来载入user */
public void prepare() throws Exception {
if( id==0 ) {
user = new User();
} else {
user = service.findUserById(id);
}
}
public Object getModel() {
return user;
}
/** 创建或更新user,然后查看该user */
public String update() {
if( id==0 ) {
service.create(user);
} else {
service.update(user);
}
return “redirect”;
}
/** 删除该user,然后跳转到默认的home 页面 */
public String delete() {
service.deleteById(id);
return “home”;
}
/** 显示页面,让用户可以看到已存在的数据*/
public String view() {
return “view”;
}
/**显示页面,让用户可以看到已存在的数据并对其进行修改*/
public String edit() {
return “input”;
}
}
当用户想创建一个新的 user或是修改一个已存在的 user时,edit()方法就会被调用。这个方法很简单,返回“input”结果,然后就会给用户一个 HTML表单页面。该表单的 Action指向一个映射到update()方法上的 URL。update()方法实际上还可以被拆分成两个独立的方法,但是这会让 HTML表单变得复杂,而且因为通过唯一的提高效率技巧
key字段可以很容易的判断一个对象是否存在,所以这样做的意义也不大。
最后,view()方法是一个简单的 pass-through方法,它将页面跳转到一个显示 user的页面上去。delete()方法会根据特定的 id来删除user,然后返回到用户默认的 home页面。
所有这些方法基本上都没有什么逻辑实现,所以很容易就会被误解为什么什么都没做。实际上,这里还是有功能实现的,但是它是一个横切的关注点,并且被重构到了 prepare()方法中。对于edit(),update()和 view()这几个方法而言,如果有模型存在的话,那么它就要获取到该模型;如果没有的话,那么就要创建一个模型。
这种 Action实际上还是很简单的,而且很容易参数化,以用于不同的模型类和 service。有了这样的微架构,那么在开发 CRUD的应用时,最复杂的事情就变成创建页面模板了。