【CSII-PE】List类型参数请求与接受实现分析

我们在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@王大仙
 

猜你喜欢

转载自blog.csdn.net/joxlin/article/details/81625919