相关文章:
- 多模式 Web 应用开发记录<一>背景&全局变量优化
- 多模式 Web 应用开发记录<二>自己动手写一个 Struts
开头先看一个简单的例子,这是 ftl 文件的一个表单:
<form id="validateForm" action="#" method="post">
<div style="padding: 10px 0;" class="ft14">处理信息:</div>
<table class="inputTable">
<tr>
<th>
备注:
</th>
<td>
<textarea cols="10" rows="3" name="repairVO.remark" class="formTextarea" ></textarea>
</td>
</tr>
</table>
<div class="buttonArea" id="option">
<input class="btn btn-big" action="doProcess.action" type="submit" value="处理"/>
<input class="btn btn-big" action="doCancel.action" type="submit" value="取消"/>
<input type="hidden" name="bizId" value="${(bizId)!}"></input>
</div>
</form>
单看这个表单,只会传递两个参数:bizId
和 repairVO.remark
。所以后端 Struts 的 Action 的成员属性会接收 remark
:
private RepairVO repairVO;
public String doProcess() {
try {
logger.info("repairVO:{}" , JsonUtils.toJsonString(repairVO));
//...其他业务处理
return SUCCESS;
} catch (Exception e) {
return ERROR;
}
}
正常来说这里的 repairVO
应该只有 remark
属性有数据,可我这里调试发现 repairVO
居然还有其他的属性也有值。
经过排查发现是因为 Action 里面对于 repairVO
有个特殊的 setter 方法:
public void setRepairVO(RepairVO repairVO) {
if(repairVO==null){
repairVO = new RepairVO();
}
if(repairVO.getId()==null){
repairVO.setId(this.getBizId());
}
repairVO.setOrgId(this.getAdmin().getOrgId());
repairVO.setOperateTime(new Date());
repairVO.setOperatorId(this.getAdmin().getId());
repairVO.setOperatorName(this.getAdmin().getUsername());
this.repairVO = repairVO;
}
这是因为在 Struts2 中,当一个 Action 被调用时,Struts2 会尝试将 HTTP 请求的参数绑定到 Action 的属性。这个过程是通过调用 Action 的 setter 方法完成的。相当于这里对 repairVO
进行了预初始化属性。
这个原理其实也很容易理解,在多模式 Web 应用开发记录<二>自己动手写一个 Struts中实现的 Struts 中是通过反射直接对属性进行赋值,从而进行参数绑定:
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String actionPath = req.getPathInfo();
if (actionPath.endsWith(".jsp")) {
req.getServletContext().getNamedDispatcher("jsp").forward(req, resp);
} else {
Class<? extends Action> clazz = actions.get(actionPath);
if (clazz != null) {
Action action = null;
try {
action = clazz.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
Map<String, String[]> paramMap = req.getParameterMap();
for (String paramName : paramMap.keySet()) {
String[] paramValues = paramMap.get(paramName);
for (String paramValue : paramValues) {
Field field;
try {
field = clazz.getDeclaredField(paramName);
if (field != null) {
field.setAccessible(true);
field.set(action, paramValue);
}
} catch (Exception e) {
}
}
}
String view = action.execute(req, resp);
req.getRequestDispatcher(view).forward(req, resp);
} else {
resp.sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
}
这里完全可以改成基于 setter 方法进行参数绑定,那么就可以实现跟 Struts2 类似的“预初始化属性”机制。
那么在 Spring MVC 中如何实现呢,可以直接使用 @ModelAttribute
注解:
@ModelAttribute("repairVO")
public RepairParam setup() {
RepairParam repairVO = new RepairParam();
User currentUser = getCurrentUser();
repairVO.setOrgId(currentUser.getOrgId());
repairVO.setOperateTime(new Date());
repairVO.setOperatorId(currentUser.getId());
repairVO.setOperatorName(currentUser.getUsername());
return repairVO;
}
@RequestMapping("doProcess.action")
public String doProcess(@RequestParam("bizId") String bizId, @ModelAttribute("repairVO") RepairParam repairVO, Model model) {
//...其他业务逻辑
}
这里 doProcess
函数的 repairVO
对象就包括 setup
函数的预初始化属性和请求中的 repairVO.xxx 属性。也就实现了预初始化属性的设置。
欢迎关注公众号: