相关文章:
- 多模式 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 中是通过反射直接对属性进行赋值,从而进行参数绑定:
@Overrideprotected 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 属性。也就实现了预初始化属性的设置。
欢迎关注公众号: