struts源码分析(4)-----form表单的双向映射!

23 篇文章 0 订阅
在struts中,我们都知道他拥有自己的标记,如下:

  1. <html:form action="coreyForm">
  2. <html:text property="name" />
  3. <html:password property="pwd" />
  4. <html:submit value="submit" />
  5. </html:form>
最后,他们会生成如下html标签:

  1. <form action="./coreyForm.do">
  2. <input type="text" name="name" />
  3. <input type="password" name="pwd" />
  4. <input type="submit" value="submit" />
  5. </form>
有的时候,我们会选择不采用struts的html便签等等,其实这样的话,不是一个很好的处理,因为struts的标签不仅仅是相对于重复代码的封装,更多的是为了配合其自身某些功能的实现;

比如,我们会问,为什么我们会使用html标签呢,他真的比我们直接使用html标签更加的方便吗,我们还得花费时间来学习他,其实不是这样的,你看了下面的例子就会明白;

一个页面有两个标签,分别采用的是上面的两种表单形式,我们在actionmapping的action中进行配置;

  1. <action path="/coreyForm" type="CoreyFormAction" scope="session"/>
我们可以看见我们把scope设置成为了session,我们填充这个表单进行提交到一个新的页面,然后在往回跳转,如果采用struts的html标签的话,我们可以看见我们的表单中自动填充了我们进行提交的值,而如果我们采用的是html的标签,那么这个表单是空的,默认实现的,这一节,我们来分析一个html表单与后台的对应,以及后台的值如何推到前台;


首先,在页面上,脚本会将前台页面的表单form与后台的actionForm对应起来,这是前台与后台的对应,我们来看一下如何实现的:

在RequestProcessor的process的方法中,存在下面两句代码:

  1. ActionForm form = processActionForm(request, response, mapping);
  2.         processPopulate(request, response, form, mapping);
  1. protected ActionForm processActionForm(HttpServletRequest request,
  2.         HttpServletResponse response, ActionMapping mapping) {
  3.         //生成一个新的ActionForm
  4.         ActionForm instance =
  5.             RequestUtils.createActionForm(request, mapping, moduleConfig,
  6.                 servlet);
  7.         if (instance == null) {
  8.             return (null);
  9.         }
  10.         // Store the new instance in the appropriate scope
  11.         if (log.isDebugEnabled()) {
  12.             log.debug(" Storing ActionForm bean instance in scope '"
  13.                 + mapping.getScope() + "' under attribute key '"
  14.                 + mapping.getAttribute() + "'");
  15.         }
  16.         //从ActionMapping中得到其scope,保存到设置的范围中;
  17.         if ("request".equals(mapping.getScope())) {
  18.             request.setAttribute(mapping.getAttribute(), instance);
  19.         } else {
  20.             HttpSession session = request.getSession();
  21.             session.setAttribute(mapping.getAttribute(), instance);
  22.         }
  23.         return (instance);
  24.     }
在createActionForm方法中,我们可见如下:
  1.  ActionForm instance =
  2.             //在其范围域中查找ActionForm对象,如果存在则复用,
  3.             lookupActionForm(request, attribute, mapping.getScope());
  4.          
  5.         if ((instance != null) && config.canReuse(instance)) {
  6.             return (instance);
  7.         }
  8.          //如果不存在,则重新生成新的;
  9.         return createActionForm(config, servlet);
在populate方法中,有如下:
  1.   如果不是上传的文件,那么:
  2.  if (!isMultipart) {
  3.             names = request.getParameterNames();
  4.         }
  5.         
  6.         //得到该页面提交的参数
  7.         while (names.hasMoreElements()) {
  8.             String name = (String) names.nextElement();
  9.             String stripped = name;
  10.             if (prefix != null) {
  11.                 if (!stripped.startsWith(prefix)) {
  12.                     continue;
  13.                 }
  14.                 stripped = stripped.substring(prefix.length());
  15.             }
  16.             if (suffix != null) {
  17.                 if (!stripped.endsWith(suffix)) {
  18.                     continue;
  19.                 }
  20.                 stripped =
  21.                     stripped.substring(0, stripped.length() - suffix.length());
  22.             }
  23.             Object parameterValue = null;
  24.             if (isMultipart) {
  25.                 parameterValue = multipartParameters.get(name);
  26.             } else {
  27.                 parameterValue = request.getParameterValues(name);
  28.             }
  29.             // Populate parameters, except "standard" struts attributes
  30.             // such as 'org.apache.struts.action.CANCEL'
  31.             if (!(stripped.startsWith("org.apache.struts."))) {
  32.                 properties.put(stripped, parameterValue);
  33.             }
  34.         }
  35.         // 将参数和actionForm的属性对应起来;形成了页面数据和后台的对应;
  36.         try {
  37.             BeanUtils.populate(bean, properties);
  38.         } catch (Exception e) {
  39.             throw new ServletException("BeanUtils.populate", e);
  40.         } finally {
  41.             if (multipartHandler != null) {
  42.                 // Set the multipart request handler for our ActionForm.
  43.                 // If the bean isn't an ActionForm, an exception would have been
  44.                 // thrown earlier, so it's safe to assume that our bean is
  45.                 // in fact an ActionForm.
  46.                 ((ActionForm) bean).setMultipartRequestHandler(multipartHandler);
  47.             }
  48.         }
  49.     }

我们可以看见知道这里为止,前台页面和后台的数据对应起来了;

那么,我们怎么可能使得修改后台的actionForm表单的数据使得前台和后台的数据对应起来呢?就像我们一开始的时候那样,actionForm是session范围的,使用了struts的标签我们依然能够使得其自动填充表单;

那么我们就要分析一下struts的标签了;

首先,在页面上,我们会解析<html:form action=“” />标签,表单控件要与后台对应起来,那么这个对应的单位是form,所以,空间应用都被包含在一个form中;

  1. public class FormTag extends TagSupport
form表单据称于TagSupport,他为整个表单与后台的对应做了很多预先的工作;
  1.   public int doStartTag() throws JspException {
  2.         postbackAction = null;
  3.         //查找表单的范围域,bean的名字,以及类型;
  4.         this.lookup();
  5.         //生成html的form控件的text;
  6.         StringBuffer results = new StringBuffer();
  7.         results.append(this.renderFormStartElement());
  8.         results.append(this.renderToken());
  9.         //将<form action="XXXX" 等输入到页面上;
  10.         TagUtils.getInstance().write(pageContext, results.toString());
  11.         //将当前的form信息保存在pageContext下面
  12.         //使得在form标记结束之前,之间的表单控件能够找到
  13.         //自己所属的form,以及后台的actionForm;
  14.         pageContext.setAttribute(Constants.FORM_KEY, this,
  15.             PageContext.REQUEST_SCOPE);
  16.         this.initFormBean();
  17.         return (EVAL_BODY_INCLUDE);
  18.     }
lookUp()
  1.  protected void lookup() throws JspException {
  2.         // 首先查找到模块;
  3.         moduleConfig = TagUtils.getInstance().getModuleConfig(pageContext);
  4.         if (moduleConfig == null) {
  5.             JspException e =
  6.                 new JspException(messages.getMessage("formTag.collections"));
  7.             pageContext.setAttribute(Globals.EXCEPTION_KEY, e,
  8.                 PageContext.REQUEST_SCOPE);
  9.             throw e;
  10.         }
  11.         //得到其标签的action属性,我们需要根据他在模块配置信息中找到
  12.         //对应的配置信息;
  13.         String calcAction = this.action;
  14.         // If the action is not specified, use the original request uri
  15.         if (this.action == null) {
  16.             HttpServletRequest request =
  17.                 (HttpServletRequest) pageContext.getRequest();
  18.             postbackAction =
  19.                 (String) request.getAttribute(Globals.ORIGINAL_URI_KEY);
  20.             String prefix = moduleConfig.getPrefix();
  21.             if (postbackAction != null && prefix.length() > 0 && postbackAction.startsWith(prefix)) {
  22.                 postbackAction = postbackAction.substring(prefix.length());
  23.             }
  24.             calcAction = postbackAction;
  25.         } else {
  26.             //找到了对应的action配置信息;
  27.             ActionConfig actionConfig = moduleConfig.findActionConfigId(this.action);
  28.             if (actionConfig != null) {
  29.                 this.action = actionConfig.getPath();
  30.                 calcAction = this.action;
  31.             }
  32.         }
  33.         servlet =
  34.             (ActionServlet) pageContext.getServletContext().getAttribute(Globals.ACTION_SERVLET_KEY);
  35.         // Look up the action mapping we will be submitting to
  36.         String mappingName =
  37.             TagUtils.getInstance().getActionMappingName(calcAction);
  38.         //得到了actionMapping信息;
  39.         mapping = (ActionMapping) moduleConfig.findActionConfig(mappingName);
  40.         if (mapping == null) {
  41.             JspException e =
  42.                 new JspException(messages.getMessage("formTag.mapping",
  43.                         mappingName));
  44.  
  45.             pageContext.setAttribute(Globals.EXCEPTION_KEY, e,
  46.                 PageContext.REQUEST_SCOPE);
  47.             throw e;
  48.         }
  49.         // 得到了表单的配置信息;
  50.         FormBeanConfig formBeanConfig =
  51.             moduleConfig.findFormBeanConfig(mapping.getName());
  52.       
  53.         if (formBeanConfig == null) {
  54.             JspException e = null;
  55.             if (mapping.getName() == null) {
  56.                 e = new JspException(messages.getMessage("formTag.name", calcAction));
  57.             } else {
  58.                 e = new JspException(messages.getMessage("formTag.formBean",
  59.                             mapping.getName(), calcAction));
  60.             }
  61.             pageContext.setAttribute(Globals.EXCEPTION_KEY, e,
  62.                 PageContext.REQUEST_SCOPE);
  63.             throw e;
  64.         }
  65.         
  66.         beanName = mapping.getAttribute();
  67.         beanScope = mapping.getScope();
  68.         beanType = formBeanConfig.getType();
  69.     }
initFormBean:初始化了表单对象;
  1. protected void initFormBean()
  2.         throws JspException {
  3.         int scope = PageContext.SESSION_SCOPE;
  4.         if ("request".equalsIgnoreCase(beanScope)) {
  5.             scope = PageContext.REQUEST_SCOPE;
  6.         }
  7.         Object bean = pageContext.getAttribute(beanName, scope);
  8.         if (bean == null) {
  9.             // New and improved - use the values from the action mapping
  10.             bean =
  11.                 RequestUtils.createActionForm((HttpServletRequest) pageContext
  12.                     .getRequest(), mapping, moduleConfig, servlet);
  13.             if (bean instanceof ActionForm) {
  14.                 ((ActionForm) bean).reset(mapping,
  15.                     (HttpServletRequest) pageContext.getRequest());
  16.             }
  17.             if (bean == null) {
  18.                 throw new JspException(messages.getMessage("formTag.create",
  19.                         beanType));
  20.             }
  21.             pageContext.setAttribute(beanName, bean, scope);
  22.         }
  23.         pageContext.setAttribute(Constants.BEAN_KEY, bean,
  24.             PageContext.REQUEST_SCOPE);
  25.     }
然后接下来就是解析<html:text property="name" />之类的表单控件呢:

  1. public class TextTag extends BaseFieldTag {
  2.     /**
  3.      * Construct a new instance of this tag.
  4.      */
  5.     public TextTag() {
  6.         super();
  7.         this.type = "text";
  8.         doReadonly = true;
  9.     }
  10. }
  1.  public int doStartTag() throws JspException {
  2.         //将html写入到页面上去;
  3.         TagUtils.getInstance().write(this.pageContext, this.renderInputElement());
  4.         return (EVAL_BODY_TAG);
  5.     }
  1. protected String renderInputElement()
  2.         throws JspException {
  3.         StringBuffer results = new StringBuffer("<input");
  4.         prepareAttribute(results, "type"this.type);
  5.         prepareAttribute(results, "name", prepareName());
  6.         prepareAttribute(results, "accesskey", getAccesskey());
  7.         prepareAttribute(results, "accept", getAccept());
  8.         prepareAttribute(results, "maxlength", getMaxlength());
  9.         prepareAttribute(results, "size", getCols());
  10.         prepareAttribute(results, "tabindex", getTabindex());
  11.         //在这里,将会到后台对应的actionForm中去拿值;
  12.         //把后台的数据推到前台来;
  13.         prepareValue(results);
  14.         results.append(this.prepareEventHandlers());
  15.         results.append(this.prepareStyles());
  16.         prepareOtherAttributes(results);
  17.         results.append(this.getElementClose());
  18.         return results.toString();
  19.     }
  1. protected void prepareValue(StringBuffer results)
  2.         throws JspException {
  3.         results.append(" value=/"");
  4.     
  5.         //如果设置了值的属性;那么显示他;
  6.         if (value != null) {
  7.             results.append(this.formatValue(value));
  8.         //如果没有的话,那么从后台数据中取得;
  9.         } else if (redisplay || !"password".equals(type)) {
  10.             Object value =
  11.                 TagUtils.getInstance().lookup(pageContext, name, property, null);
  12.             results.append(this.formatValue(value));
  13.         }
  14.         results.append('"');
  15.     }
  1. public Object lookup(PageContext pageContext, String name, String property,
  2.         String scope) throws JspException {
  3.         //从页面中取得bean
  4.         Object bean = lookup(pageContext, name, scope);
  5.         if (bean == null) {
  6.             JspException e = null;
  7.             if (scope == null) {
  8.                 e = new JspException(messages.getMessage("lookup.bean.any", name));
  9.             } else {
  10.                 e = new JspException(messages.getMessage("lookup.bean", name,
  11.                             scope));
  12.             }
  13.             saveException(pageContext, e);
  14.             throw e;
  15.         }
  16.         if (property == null) {
  17.             return bean;
  18.         }
  19.         
  20.         try { 
  21.             //从表单中取得该property标记的属性的值,返回,输出到页
  22.             //面;
  23.             return PropertyUtils.getProperty(bean, property);
  24.         } catch (IllegalAccessException e) {
  25.             saveException(pageContext, e);
  26.             throw new JspException(messages.getMessage("lookup.access",
  27.                     property, name));
  28.         } catch (IllegalArgumentException e) {
  29.             saveException(pageContext, e);
  30.             throw new JspException(messages.getMessage("lookup.argument",
  31.                     property, name));
  32.         } catch (InvocationTargetException e) {
  33.             Throwable t = e.getTargetException();
  34.             if (t == null) {
  35.                 t = e;
  36.             }
  37.             saveException(pageContext, t);
  38.             throw new JspException(messages.getMessage("lookup.target",
  39.                     property, name));
  40.         } catch (NoSuchMethodException e) {
  41.             saveException(pageContext, e);
  42.             String beanName = name;
  43.             // Name defaults to Contants.BEAN_KEY if no name is specified by
  44.             // an input tag. Thus lookup the bean under the key and use
  45.             // its class name for the exception message.
  46.             if (Constants.BEAN_KEY.equals(name)) {
  47.                 Object obj = pageContext.findAttribute(Constants.BEAN_KEY);
  48.                 if (obj != null) {
  49.                     beanName = obj.getClass().getName();
  50.                 }
  51.             }
  52.             throw new JspException(messages.getMessage("lookup.method",
  53.                     property, beanName));
  54.         }
  55.     }
最后,会为form元素“封口”
  1. public int doEndTag() throws JspException {
  2.         // 从页面上下文中消除掉当前form的标记
  3.         pageContext.removeAttribute(Constants.BEAN_KEY,
  4.             PageContext.REQUEST_SCOPE);
  5.         pageContext.removeAttribute(Constants.FORM_KEY,
  6.             PageContext.REQUEST_SCOPE);
  7.         //封闭form标记;
  8.         StringBuffer results = new StringBuffer("</form>");
  9.         // 显示当前的焦点;
  10.         if (this.focus != null) {
  11.             results.append(this.renderFocusJavascript());
  12.         }
  13.         // 显示到页面上去;
  14.         JspWriter writer = pageContext.getOut();
  15.         try {
  16.             writer.print(results.toString());
  17.         } catch (IOException e) {
  18.             throw new JspException(messages.getMessage("common.io", e.toString()));
  19.         }
  20.         postbackAction = null;
  21.         // 继续执行该页面;
  22.         return (EVAL_PAGE);
  23.     }
从上面,我们可见在struts里面是不支持表单嵌套的,表单的顺序定义还是可以的;这样不会造成pageContext页面标记的被覆盖等现象;


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值