在struts中,我们都知道他拥有自己的标记,如下:
<html:form action="coreyForm">
<html:text property="name" />
<html:password property="pwd" />
<html:submit value="submit" />
</html:form>

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

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

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

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


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

在RequestProcessor的process的方法中,存在下面两句代码:
ActionForm form = processActionForm(request, response, mapping);
processPopulate(request, response, form, mapping);
protected ActionForm processActionForm(HttpServletRequest request,
HttpServletResponse response, ActionMapping mapping) {
//生成一个新的ActionForm
ActionForm instance =
RequestUtils.createActionForm(request, mapping, moduleConfig, ervlet);
if (instance == null) {
return (null);
}

// Store the new instance in the appropriate scope
if (log.isDebugEnabled()) {
log.debug(" Storing ActionForm bean instance in scope \'"
+ mapping.getScope() + "\' under attribute key \'"
+ mapping.getAttribute() + "\'");
}
//从ActionMapping中得到其scope,保存到设置的范围中;
if ("request".equals(mapping.getScope())) {
request.setAttribute(mapping.getAttribute(), instance);
} else {
HttpSession session = request.getSession();
session.setAttribute(mapping.getAttribute(), instance);
}

return (instance);
}
在createActionForm方法中,我们可见如下:
ActionForm instance =

//在其范围域中查找ActionForm对象,如果存在则复用,
lookupActionForm(request, attribute, mapping.getScope());


if ((instance != null) && config.canReuse(instance)) {
return (instance);
}
//如果不存在,则重新生成新的;
return createActionForm(config, servlet);
在populate方法中,有如下:

如果不是上传的文件,那么:

if (!isMultipart) {
names = request.getParameterNames();
}

//得到该页面提交的参数
while (names.hasMoreElements()) {
String name = (String) names.nextElement();
String stripped = name;

if (prefix != null) {
if (!stripped.startsWith(prefix)) {
continue;
}

stripped = stripped.substring(prefix.length());
}

if (suffix != null) {
if (!stripped.endsWith(suffix)) {
continue;
}

stripped =
stripped.substring(0, stripped.length() - suffix.length());
}

Object parameterValue = null;

if (isMultipart) {
parameterValue = multipartParameters.get(name);
} else {
parameterValue = request.getParameterValues(name);
}

// Populate parameters, except "standard" struts attributes
// such as \'org.apache.struts.action.CANCEL\'
if (!(stripped.startsWith("org.apache.struts."))) {
properties.put(stripped, parameterValue);
}
}

// 将参数和actionForm的属性对应起来;形成了页面数据和后台的对应;
try {
BeanUtils.populate(bean, properties);
} catch (Exception e) {
throw new ServletException("BeanUtils.populate", e);
} finally {
if (multipartHandler != null) {
// Set the multipart request handler for our ActionForm.
// If the bean isn\'t an ActionForm, an exception would have been
// thrown earlier, so it\'s safe to assume that our bean is
// in fact an ActionForm.
((ActionForm) bean).setMultipartRequestHandler(multipartHandler);
}
}
}

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

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

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

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


public class FormTag extends TagSupport
form表单据称于TagSupport,他为整个表单与后台的对应做了很多预先的工作;

public int doStartTag() throws JspException {

postbackAction = null;

//查找表单的范围域,bean的名字,以及类型;
this.lookup();

//生成html的form控件的text;
StringBuffer results = new StringBuffer();

results.append(this.renderFormStartElement());

results.append(this.renderToken());
//将<form action="XXXX" 等输入到页面上;
TagUtils.getInstance().write(pageContext, results.toString());

//将当前的form信息保存在pageContext下面
//使得在form标记结束之前,之间的表单控件能够找到
//自己所属的form,以及后台的actionForm;
pageContext.setAttribute(Constants.FORM_KEY, this,
PageContext.REQUEST_SCOPE);

this.initFormBean();

return (EVAL_BODY_INCLUDE);
}

lookUp()

protected void lookup() throws JspException {

// 首先查找到模块;
moduleConfig = TagUtils.getInstance().getModuleConfig(pageContext);

if (moduleConfig == null) {
JspException e =
new JspException(messages.getMessage("formTag.collections"));

pageContext.setAttribute(Globals.EXCEPTION_KEY, e,
PageContext.REQUEST_SCOPE);
throw e;
}
//得到其标签的action属性,我们需要根据他在模块配置信息中找到
//对应的配置信息;
String calcAction = this.action;

// If the action is not specified, use the original request uri
if (this.action == null) {
HttpServletRequest request =
(HttpServletRequest) pageContext.getRequest();
postbackAction =
(String) request.getAttribute(Globals.ORIGINAL_URI_KEY);

String prefix = moduleConfig.getPrefix();
if (postbackAction != null && prefix.length() > 0 && postbackAction.startsWith(prefix)) {
postbackAction = postbackAction.substring(prefix.length());
}
calcAction = postbackAction;
} else {
//找到了对应的action配置信息;
ActionConfig actionConfig = moduleConfig.findActionConfigId(this.action);
if (actionConfig != null) {
this.action = actionConfig.getPath();
calcAction = this.action;
}
}

servlet =
(ActionServlet) pageContext.getServletContext().getAttribute(Globals.ACTION_SERVLET_KEY);

// Look up the action mapping we will be submitting to
String mappingName =
TagUtils.getInstance().getActionMappingName(calcAction);
//得到了actionMapping信息;
mapping = (ActionMapping) moduleConfig.findActionConfig(mappingName);

if (mapping == null) {
JspException e =
new JspException(messages.getMessage("formTag.mapping",
mappingName));

pageContext.setAttribute(Globals.EXCEPTION_KEY, e,
PageContext.REQUEST_SCOPE);
throw e;
}

// 得到了表单的配置信息;
FormBeanConfig formBeanConfig =
moduleConfig.findFormBeanConfig(mapping.getName());

if (formBeanConfig == null) {
JspException e = null;

if (mapping.getName() == null) {
e = new JspException(messages.getMessage("formTag.name", calcAction));
} else {
e = new JspException(messages.getMessage("formTag.formBean",
mapping.getName(), calcAction));
}

pageContext.setAttribute(Globals.EXCEPTION_KEY, e,
PageContext.REQUEST_SCOPE);
throw e;
}


beanName = mapping.getAttribute();
beanScope = mapping.getScope();
beanType = formBeanConfig.getType();
}
initFormBean:初始化了表单对象;

protected void initFormBean()
throws JspException {
int scope = PageContext.SESSION_SCOPE;

if ("request".equalsIgnoreCase(beanScope)) {
scope = PageContext.REQUEST_SCOPE;
}

Object bean = pageContext.getAttribute(beanName, scope);

if (bean == null) {
// New and improved - use the values from the action mapping
bean =
RequestUtils.createActionForm((HttpServletRequest) pageContext
.getRequest(), mapping, moduleConfig, servlet);

if (bean instanceof ActionForm) {
((ActionForm) bean).reset(mapping,
(HttpServletRequest) pageContext.getRequest());
}

if (bean == null) {
throw new JspException(messages.getMessage("formTag.create",
beanType));
}

pageContext.setAttribute(beanName, bean, scope);
}

pageContext.setAttribute(Constants.BEAN_KEY, bean,
PageContext.REQUEST_SCOPE);
}
然后接下来就是解析<html:text property="name" />之类的表单控件呢:


public class TextTag extends BaseFieldTag {
/**
* Construct a new instance of this tag.
*/
public TextTag() {
super();
this.type = "text";
doReadonly = true;
}
}

public int doStartTag() throws JspException {
//将html写入到页面上去;
TagUtils.getInstance().write(this.pageContext, this.renderInputElement());

return (EVAL_BODY_TAG);
}
protected String renderInputElement()
throws JspException {
StringBuffer results = new StringBuffer("<input");
prepareAttribute(results, "type", this.type);
prepareAttribute(results, "name", prepareName());
prepareAttribute(results, "accesskey", getAccesskey());
prepareAttribute(results, "accept", getAccept());
prepareAttribute(results, "maxlength", getMaxlength());
prepareAttribute(results, "size", getCols());
prepareAttribute(results, "tabindex", getTabindex());
//在这里,将会到后台对应的actionForm中去拿值;
//把后台的数据推到前台来;
prepareValue(results);
results.append(this.prepareEventHandlers());
results.append(this.prepareStyles());
prepareOtherAttributes(results);
results.append(this.getElementClose());

return results.toString();
}
protected void prepareValue(StringBuffer results)
throws JspException {
results.append(" value=\\"");

//如果设置了值的属性;那么显示他;
if (value != null) {
results.append(this.formatValue(value));
//如果没有的话,那么从后台数据中取得;
} else if (redisplay || !"password".equals(type)) {
Object value =
TagUtils.getInstance().lookup(pageContext, name, property, null);

results.append(this.formatValue(value));
}

results.append(\'"\');
}
public Object lookup(PageContext pageContext, String name, String property,
String scope) throws JspException {
//从页面中取得bean
Object bean = lookup(pageContext, name, scope);

if (bean == null) {
JspException e = null;

if (scope == null) {
e = new JspException(messages.getMessage("lookup.bean.any", name));
} else {
e = new JspException(messages.getMessage("lookup.bean", name,
scope));
}

saveException(pageContext, e);
throw e;
}

if (property == null) {
return bean;
}


try {
//从表单中取得该property标记的属性的值,返回,输出到页
//面;
return PropertyUtils.getProperty(bean, property);
} catch (IllegalAccessException e) {
saveException(pageContext, e);
throw new JspException(messages.getMessage("lookup.access",
property, name));
} catch (IllegalArgumentException e) {
saveException(pageContext, e);
throw new JspException(messages.getMessage("lookup.argument",
property, name));
} catch (InvocationTargetException e) {
Throwable t = e.getTargetException();

if (t == null) {
t = e;
}

saveException(pageContext, t);
throw new JspException(messages.getMessage("lookup.target",
property, name));
} catch (NoSuchMethodException e) {
saveException(pageContext, e);

String beanName = name;

// Name defaults to Contants.BEAN_KEY if no name is specified by
// an input tag. Thus lookup the bean under the key and use
// its class name for the exception message.
if (Constants.BEAN_KEY.equals(name)) {
Object obj = pageContext.findAttribute(Constants.BEAN_KEY);

if (obj != null) {
beanName = obj.getClass().getName();
}
}

throw new JspException(messages.getMessage("lookup.method",
property, beanName));
}
}

最后,会为form元素“封口”

public int doEndTag() throws JspException {
// 从页面上下文中消除掉当前form的标记
pageContext.removeAttribute(Constants.BEAN_KEY,
PageContext.REQUEST_SCOPE);
pageContext.removeAttribute(Constants.FORM_KEY,
PageContext.REQUEST_SCOPE);

//封闭form标记;
StringBuffer results = new StringBuffer("</form>");

// 显示当前的焦点;
if (this.focus != null) {
results.append(this.renderFocusJavascript());
}

// 显示到页面上去;
JspWriter writer = pageContext.getOut();

try {
writer.print(results.toString());
} catch (IOException e) {
throw new JspException(messages.getMessage("common.io", e.toString()));
}

postbackAction = null;

// 继续执行该页面;
return (EVAL_PAGE);
}
从上面,我们可见在struts里面是不支持表单嵌套的,表单的顺序定义还是可以的;这样不会造成pageContext页面标记的被覆盖等现象;