ModelDriven需要在action中实现ModelDriven接口,然后重写getModel()方法,在执行action的时候会先执行getModel方法,获取封装的对象。
有如下实体类:
public class Offer {
private Integer offerId; //提供者Id
private String offerName; //提供者名称
private String offerMail; //提供者邮箱
private String offerMobile; //提供者手机号
public Integer getOfferId() {
return offerId;
}
public void setOfferId(Integer offerId) {
this.offerId = offerId;
}
public String getOfferName() {
return offerName;
}
public void setOfferName(String offerName) {
this.offerName = offerName == null ? null : offerName.trim();
}
public String getOfferMail() {
return offerMail;
}
public void setOfferMail(String offerMail) {
this.offerMail = offerMail == null ? null : offerMail.trim();
}
public String getOfferMobile() {
return offerMobile;
}
public void setOfferMobile(String offerMobile) {
this.offerMobile = offerMobile == null ? null : offerMobile.trim();
}
}
与Offer相对应的action,OfferAction:
public class OfferAction extends WebActionSupport implements ModelDriven<Offer> {
@Autowired
private OfferManager offerManager;
private Offer Offer;
private Integer pageSize = 15;
/**
* 修改或新增
*
* @return
* @throws Exception
*/
public String save() throws Exception {
String errorMessage = validateForm();
if(StringUtils.isBlank(errorMessage)){
if (rmOffer.getOfferId() != null) {
Offer dataOffer = offerManager.selectByPrimaryKey(offer.getOfferId());
dataOffer = offer;
offerManager.update(dataOffer);
WebUtil.returnJSON(response, "{\"successSign\" : true}", "json");
} else {
offerManager.add(offer);
WebUtil.returnJSON(response, "{\"successSign\" : true}", "json");
}
}else{
String jsonData = "{\"successSign\":false,\"errorMessage\":" + "\"" +errorMessage + "\""+ "}";
WebUtil.returnJSON(response, jsonData, "json");
}
return null;
}
/**
* 修改页面赋值
*
* @return
* @throws Exception
*/
public String input() throws Exception {
if (offer.getOfferId() != null) {
Offer dataOffer = offerManager.selectByPrimaryKey(offer.getOfferId());
request.setAttribute("dataOffer", dataOffer);
}
return "input";
}
private String validateForm() {
if (StringUtils.isBlank(offer.getOfferName())) {
return "提供者名称不能为空";
} else if (offer.getOfferName().length() < 2 || offer.getOfferName().length() > 20) {
return "提供者名称长度为2~20个字符";
}
if (StringUtils.isBlank(offer.getOfferMail())) {
return "提供者邮箱不能为空";
} else if (!offer.getOfferMail().matches("^[\\w-]+(\\.[\\w-]+)*@[\\w-]+(\\.[\\w-]+)+$")) {
return "提供者邮箱格式不正确";
}
if (StringUtils.isBlank(offer.getOfferMobile())) {
return "提供者电话不能为空";
} else if (!offer.getOfferMobile().matches("((\\d{11})|^((\\d{7,8})|(\\d{4}|\\d{3})-(\\d{7,8})|(\\d{4}|\\d{3})-(\\d{7,8})-(\\d{4}|\\d{3}|\\d{2}|\\d{1})|(\\d{7,8})-(\\d{4}|\\d{3}|\\d{2}|\\d{1}))$)")) {//支持手机号码,3-4位区号,7-8位直播号码,1-4位分机号
return "提供者电话格式不对";
}
return null;
}
@Override
public Offer getModel() {
if (offer == null) {
offer = new Offer();
}
return offer;
}
public Offer getOffer() {
return offer;
}
public void setOffer(Offer offer) {
this.offer = offer;
}
}
对应的添加或修改的jsp:
<form id="sendForm" name="sendForm" class="search-form" action="">
<input type="hidden" name="offerId" value="${dataRmOffer.offerId}">
<div class="search-box" style="clear: both;margin:20px 0;">
<label for="offerId">提供者名称:</label>
<input type="text" name="offerName" value="${dataOffer.offerName}">
</div>
<div class="search-box" style="clear: both;margin:20px 0;">
<label for="offerMail">提供者邮箱:</label>
<input type="text" name="offerMail" value="${dataOffer.offerMail}">
</div>
<div class="search-box" style="clear: both;margin:20px 0;">
<label for="offerMobile">提供者手机号:</label>
<input type="text" name="offerMobile" value="${dataOffer.offerMobile}">
</div>
<div style="clear: both;padding-top:20px;" >
<a href="javascript:void(0)" id="submit-btn" class="button button-blue button-m">确定</a>
<a href="#content/rm-offer.action" id="cancel-btn" class="button button-blue button-m">取消</a>
</div>
</form>
在执行execute方法前(虽然例子里面没有execute方法),会先执行getModel()方法,其实在执行action中相关方法前都会先执行getModel()方法获取相关对象。这种方式就省去了从前台一个个拿表单数据的麻烦,不用再自己new 对象一个个赋值,使得代码更简洁一点,出符合面向对象的思想。
其中有一个隐含的bug就是在执行更新方法的时候,也就是例中save()方法带offerId的情况,input()方法要从数据库中取值赋值到input页面,要先自己从数据库中查到这个Offer,而不能直接引用getModel方法获得的对象,因为getModel中总是一个新的从前台封装好过来的对象(当然不是前台来封装,应该说struts来封装),直接引用会使得input页面的text框中无数据(但id是有的),原因就是前台使用了input页面封装的Offer,只而跳转到input页面只包含了id属性。解决办法就是如上所说,自己查到offer对象,返回查到的对象。当然,ModelDrivenInterceptor提供了一个配置参数:refreshModelBeforeResult,只要把它定义为true,上术问题也能解决,意思就是把旧的model对象从ValueStack中移除,然后再把新的model对象压入ValueStack。
ModelDriven的机制:
先看看ModelDriven的原码~
public class ModelDrivenInterceptor extends AbstractInterceptor {
protected boolean refreshModelBeforeResult = false;
public void setRefreshModelBeforeResult(boolean val) {
this.refreshModelBeforeResult = val;
}
@Override
public String intercept(ActionInvocation invocation) throws Exception {
Object action = invocation.getAction();
if (action instanceof ModelDriven) {
ModelDriven modelDriven = (ModelDriven) action;
ValueStack stack = invocation.getStack();
Object model = modelDriven.getModel();
if (model != null) {
stack.push(model);
}
if (refreshModelBeforeResult) {
invocation.addPreResultListener(new RefreshModelBeforeResult(modelDriven, model));
}
}
return invocation.invoke();
}
这段代码是ModelDrivenInterceptor中的,他是缺省拦截器链的一部分,执行action的时候会先判断该action是否实现了modelDriven接口,如果是,执行getModel()方法,并把值压入值栈。下面这个方法就是刚才说的refreshModelBeforeResult属性。
protected static class RefreshModelBeforeResult implements PreResultListener {
private Object originalModel = null;
protected ModelDriven action;
public RefreshModelBeforeResult(ModelDriven action, Object model) {
this.originalModel = model;
this.action = action;
}
public void beforeResult(ActionInvocation invocation, String resultCode) {
ValueStack stack = invocation.getStack();
CompoundRoot root = stack.getRoot();
boolean needsRefresh = true;
Object newModel = action.getModel();
// Check to see if the new model instance is already on the stack
for (Object item : root) {
if (item.equals(newModel)) {
needsRefresh = false;
break;
}
}
// Add the new model on the stack
if (needsRefresh) {
// Clear off the old model instance
if (originalModel != null) {
root.remove(originalModel);
}
if (newModel != null) {
stack.push(newModel);
}
}
}
}
By the way:那个invoke()方法, 就是通知struts2接着干下面的事情比如调用下一个拦截器或执行下一个Action。
学习的时候如下博客给了很大帮助:http://blog.csdn.net/li_tengfei/article/details/6098145