我们在web页面提交的数据大部分时候都是单个的,但是有是有需要提交一个集合(List),在PE中的做法和Struts2的做法差不多。比如我们要提交一个名为RoleList的列表对象(元素为Map),那么我们可以采用以下形式 列表名[索引].属性 的形式:
<input name="RoleList[0].Name" type="text" />
<input name="RoleList[0].MobilePhone" type="text" />
<input name="RoleList[0].PaperNo" type="text" />
<input name="RoleList[0].Role" type="text" />
<input name="RoleList[1].Name" type="text" />
<input name="RoleList[1].MobilePhone" type="text" />
<input name="RoleList[1].PaperNo" type="text" />
<input name="RoleList[1].Role" type="text" />
这样,我们在Action中就可以直接使用context.getData("RoleList")来获取这个列表元素。
其中transaction中列表项用field-list标签表示:
<transaction id="PbocAutoApprove" template="publicTwoPhaseTrsTemplate">
<description>
@Description:征信自动审批
@author wangdaxian
@version 1.0
@remark
@fromPages
</description>
<actions>
<ref name="action">PbocAutoApproveAction</ref>
</actions>
<fields>
<field name="OrganId">NotEmpty</field>
<field name="SerialNo">NotEmpty</field>
<field name="Channel">NotEmpty</field>
<field name="TransactionNo">NotEmpty</field>
<field name="BizNo">NotEmpty</field>
<field-list name="RoleList" counter="" skipSignature="true">
<fields>
<field name="Name">NameStyle</field>
<field name="MobilePhone">MobilePhoneStyle</field>
<field name="PaperNo">IdNoStyle</field>
<field name="Role">NotEmpty</field>
</fields>
</field-list>
</fields>
<channels>
<channel type="http">
<param name="success">json,[_RejCode|_RejMsg]</param>
</channel>
</channels>
</transaction>
我们知道,单纯的httpform提交,是无法将单个的数据组装成List数据的,因此这之中必定需要一个组装的过程。通过跟踪manController,chian,template都没有发现这个转换的过程。最终发现这个组装的过程竟然发生在context.getData方法中。
Context的默认实现类是LocalServletContext类,查看该类的getData方法的代码即可看到相关的组装过程:
public Object getData(String name) {
Object object = super.getData(name);
if (object != null) {
return object;
} else {
Object fieldDefinition = this.getFieldDefinition(name);
//解析参数
Object value = this.getDataInternal(name, "", fieldDefinition, false);
if (value != null) {
super.setData(name, value);
return value;
} else {
return name.equals("application/stream") ? this.stream : null;
}
}
}
关键的操作就是这个getDataInternal方法,第一个参数是想要获取值的名称,第二个是路径(前缀),第三个是Field的定义对象,第四个表示是否忽略标签的condition属性。接下来分析getDataInternal方法:
首先是处理fieldDefinition是String类型时的情况,这也就是对于普通的field字段的处理。
String[] fieldList = this.request.getParameterValues(path + name);
if (fieldList != null) {
return fieldList.length == 1 ? fieldList[0] : fieldList;
} else {
if (fieldDefinition != null) {
counterName = (String) fieldDefinition;
if (counterName.equals("FileStyle") || counterName.startsWith("FileStyle{") && counterName.endsWith("}")) {
if (this.request instanceof MultipartHttpServletRequest) {
MultipartHttpServletRequest counterValue = (MultipartHttpServletRequest) this.request;
return counterValue.getFile(path + name);
} else {
throw new PeRuntimeException("request_isnot_a_valid_multipart_requst");
}
}
}
return null;
}
如果fieldDefinition是Map类型,也就是说下面还有子feild,那么就递归解析子field,并把解析结果放到map中,最终解析完成之后返回map。
if (fieldDefinition instanceof Map) {
HashMap data = new HashMap(((Map) fieldDefinition).size());
Iterator it = ((Map) fieldDefinition).keySet().iterator();
while (it.hasNext()) {
Object key = it.next();
Object value = this.getDataInternal((String) key, path, ((Map) fieldDefinition).get(key), ignoreCondition);
data.put(key, value);
}
return data;
}
对于fieldDefinition为FieldList类型时的处理和处理Map基本相同,fieldList标签表示的传递的数据是List,因此处理时返回的数据是List类型。既然是List那么元素肯定就不止一个,fieldList标签的counter属性指明了List的大小(系统调用getDataInternal获取),FieldList中的元素都会以Map类型的形式存在。FiledList元素中配置的子元素,就是Map中的键,系统迭代调用getDataInternal将数据组装为Map,然后在将Map放到List中,最终解析出完整的List对象。
通过上述的分析,基本知道了系统将请求参数转换为Map或者List的过程。这里主要需要注意的是Transaction中Fields的配置,系统将会根据配置来对请求参数做相应的转换。注意getData方法中调用了super.getData如果有值则直接返回,如果没有则组装数据,并且调用super.setData将数据保存起来(父类中getData和setDate操作一个缓存Map),也就是说数据组装最多只会发生一次,后面的操作就是直接从缓存中拿了。
另外需要注意的是:getData和所有底层使用getData的方法都可以获取到页面请求中传递过来的所有参数,但是getDataMap方法则只有在Transaction没有配置fields属性时才能拿到所有请求参数:
public Map getDataMap() {
if (this.initRequestMap) {
return new HashMap(super.getDataMap());
}
TransactionConfig tc = getTransactionConfig();
Map fields = tc == null ? null : tc.getFields();
//如果交易没有配置fields元素,那么就那请求参数
if (fields == null) {
Map requestMap = getRequestMap();
if (super.getDataMap().size() > 0)
requestMap.putAll(super.getDataMap());
super.setDataMap(requestMap);
this.initRequestMap = true;
return requestMap;
}
Map map = new HashMap(super.getDataMap());
for (Iterator it = fields.keySet().iterator(); it.hasNext();) {
String key = (String)it.next();
//调用getData方法会触发数据组装,因此getDataMap返回的数据会是组装后的数据。
map.put(key, getData(key));
}
this.initRequestMap = true;
return map;}
这里分析的是请求数据根据Transaction中的配置信息进行转换的过程,但是对于我们这些自定义的系统标签,是怎么解析成Bean放到Spring容器中的还是不太清楚,虽然之前也分析过自定义Bean标签的实现原理,但是对于这些Bean的解析过程还不清楚,接下来需要对这些配置文件解析为具体Bean的过程进行分析
by CSII@王大仙