3

在我最近的项目中,我使用 jQuery Grid,当用户单击按钮时生成动态网格。

在服务器端数据是通过使用Session.

首先,我得到Session对象并将该对象转换为gatepassDTO.

然后我正在创建一个本地对象,并将属性从旧对象复制到新对象并放入Map.

之后,我正在创建futureDTO对象并将属性从 复制gatepassDTOfutureDTO。如果我更改其中的任何内容,futureDTO则会影响包含对象的地图。

为什么会发生这种情况?

public String addOnemoreGatepass() {

        log.info("----------------Entering method addOnemoreGatepass------------");
        try {
            Object map = session.get("gatepassMap");

            GatepassDTO gatepassDTO = new GatepassDTO();
            gatepassDTO=(GatepassDTO)session.get("gatepassDTO");
            if(map!=null){

                //gatepassMap=Collections.synchronizedMap((HashMap)map);
            }
            if(truckNo!=null){
                gatepassDTO.setTruckNo(truckNo);
            }

                     if(gpDirect!=null){
                GatepassDTO tempDTO=new GatepassDTO();
                copyProperty(tempDTO,gatepassDTO);
            /*  HashMap<String,GatepassDTO> maps=null;
                if(gatepassNo.equals("1")){
                    maps=new HashMap<String, GatepassDTO>();
                local.saveMap(getRequest().getSession().getId(),maps);
                }
                maps=local.loadMap(getRequest().getSession().getId());
                maps.put(gatepassNo, tempDTO);*/
                putCachedObject("SINGLEGATEPASSCACHE", gatepassNo, tempDTO);
                gatepassMap.put(gatepassNo, tempDTO);

                //local.saveMap(getRequest().getSession().getId(),maps);
                session.put("documentType", documentType);
                session.put("gatepassMap", gatepassMap);
                return SUCCESS;
            }
            else{
                GatepassDTO tempDTO=new GatepassDTO();
                copyProperty(tempDTO,gatepassDTO);
                /*HashMap<String,GatepassDTO> maps=null;
                if(gatepassNo.equals("1")){
                    maps=new HashMap<String, GatepassDTO>();
                local.saveMap(getRequest().getSession().getId(),maps);
                }
                maps=local.loadMap(getRequest().getSession().getId());
                maps.put(gatepassNo, tempDTO);*/
                putCachedObject("SINGLEGATEPASSCACHE", gatepassNo, tempDTO);
                gatepassMap.put(gatepassNo, tempDTO);

                //local.saveMap(getRequest().getSession().getId(),maps);
                session.put("documentType", documentType);
                session.put("gatepassMap", gatepassMap);

            }
            GatepassDTO futureDTO=new GatepassDTO();
            copyProperty(futureDTO,gatepassDTO);

            DocumentDTO documentDTO =futureDTO.getDocumentDTO();
            List<GatepassGoodsDTO> goodsList=documentDTO.getGatepassGoodsList();
            int i=0;
            for(GatepassGoodsDTO goodsDTO:goodsList){
                if(goodsDTO.getRemovalType()!=null&&(goodsDTO.getRemovalType().equals("Quantity")||goodsDTO.getRemovalType().equals("Weight"))){
                goodsDTO.setPrevRemovalType(goodsDTO.getRemovalType());
                goodsDTO.setPrevGPQuantity((goodsDTO.getRemovalQuantity()!=null?goodsDTO.getRemovalQuantity():0));
                goodsDTO.setPrevGPWeight((goodsDTO.getRemovalWeight()!=null?goodsDTO.getRemovalWeight():0));
                goodsDTO.setBalanceQuantity(goodsDTO.getBalanceQuantity()-goodsDTO.getRemovalQuantity());
                goodsDTO.setBalanceWeight(goodsDTO.getBalanceWeight()-goodsDTO.getRemovalWeight());
                goodsDTO.getVehicleDetailsList().clear();
                goodsDTO.setVehicleDetailsList(goodsDTO.getDeletedVehicleDetailsList());
                goodsDTO.setDeletedVehicleDetailsList(new ArrayList<GatepassVehicleDTO>());
                goodsDTO.setRemovalType("");
                goodsDTO.setRemovalQuantity(0);
                goodsList.set(i, goodsDTO);}
                else{
                    goodsList.set(i, goodsDTO);
                }
                i++;
            }
            documentDTO.setGatepassGoodsList(goodsList);
            documentDTO.setContainerList(deletedContainerModel);
            futureDTO.setDocumentDTO(documentDTO);
            futureDTO.setTruckModel(new ArrayList<GatepassTruckDTO>());
            session.put("gatepassDTO",futureDTO);
            // setDocumentModel(null);
            // setGridModel(null);
            deletedVehicleModel.clear();
            deletedContainerModel.clear();
            // manifestNo=null;



        } catch (Exception e) {
            log.info(e.getMessage());
        } 
        log.info("---------------Ending method addOnemoreGatepass----------------");
        return SUCCESS;
    }


private void copyProperty(GatepassDTO tempDTO,GatepassDTO gatepassDTO){`enter code here`
        tempDTO.setDocumentDTO(gatepassDTO.getDocumentDTO());
        tempDTO.setTruckNo(gatepassDTO.getTruckNo());
    }

为什么会出现这个问题?这是核心 Java 问题还是 Struts2 JSON 问题?

4

2 回答 2

1

您正在使用引用(将它们视为指针)。

当您从 Session Map 获取对象时,您将获得它的引用(即使 Map 只包含引用,而不是真实对象)

gatepassDTO = (GatepassDTO)session.get("gatepassDTO");

然后实例化 tempDTO,并将 gatepassDTO.getDocumentDTO() 的引用(即引用本身)分配给 tempDTO

GatepassDTO tempDTO = new GatepassDTO();
copyProperty(tempDTO,gatepassDTO);
// that inside inside copyProperty does:
tempDTO.setDocumentDTO(gatepassDTO.getDocumentDTO());

然后实例化futureDTO,并再次分配gatepassDTO.getDocumentDTO() 的引用:

GatepassDTO futureDTO = new GatepassDTO();
copyProperty(futureDTO,gatepassDTO);
// that inside inside copyProperty does:
futureDTO.setDocumentDTO(gatepassDTO.getDocumentDTO());

此时,如果您.getDocumentDTO().setSomething();从 gatepassDTO 或 tempDTO 甚至从 futureDTO 调用,您总是在更改同一个物理对象,它仍然(引用)在 Map 中。

实际上,copyProperty 并没有复制任何东西。

这不会发生在原始类型中,行intchar,也不会发生在不可变对象中,例如IntegerString

但这总是会发生在所有其他对象上,例如您编写的 DocumentDTO。

为了防止此类问题,您需要返回对象的防御性副本,这是首选方式,或者至少手动复制每个属性,如下所示:

private void copyProperty(GatepassDTO tempDTO,GatepassDTO gatepassDTO){
    DocumentDTO src = gatepassDTO.getDocumentDTO();
    DocumentDTO dest = new DocumentDTO();
    dest.setSomething(src.getSomething());
    dest.setSomethingElse(src.getSomethingElse());
    dest.setEtc(src.getEtc());
    /* go on like that with primitives or immutables, 
    and instantiate new objects for each mutable object you find */

    tempDTO.setDocumentDTO(dest);
    tempDTO.setTruckNo(gatepassDTO.getTruckNo());
}

显然,如果您将这种逻辑放在 getter 中,您将不需要在应用程序中复制此代码片段,并且您将防止忘记某些内容或写错字的风险。


注1

为了在这种情况下为您提供帮助,Apache 就在那里。

BeanUtils.copyProperties是您要查找的内容,它将复制两个对象之间名称匹配的每个字段。


笔记2

您的代码可以被高度重构......例如:

 if(gpDirect!=null){
    GatepassDTO tempDTO = new GatepassDTO();
    copyProperty(tempDTO,gatepassDTO);
    putCachedObject("SINGLEGATEPASSCACHE", gatepassNo, tempDTO);
    gatepassMap.put(gatepassNo, tempDTO);
    session.put("documentType", documentType);
    session.put("gatepassMap", gatepassMap);
    return SUCCESS;
}else{
    GatepassDTO tempDTO = new GatepassDTO();
    copyProperty(tempDTO,gatepassDTO);
    putCachedObject("SINGLEGATEPASSCACHE", gatepassNo, tempDTO);
    gatepassMap.put(gatepassNo, tempDTO);
    session.put("documentType", documentType);
    session.put("gatepassMap", gatepassMap);
}

可以写成

GatepassDTO tempDTO = new GatepassDTO();
copyProperty(tempDTO,gatepassDTO);
putCachedObject("SINGLEGATEPASSCACHE", gatepassNo, tempDTO);
gatepassMap.put(gatepassNo, tempDTO);
session.put("documentType", documentType);
session.put("gatepassMap", gatepassMap);

if(gpDirect!=null) return SUCCESS;

花点时间清理它,它将为您节省时间和将来的麻烦。

于 2013-07-16T10:14:49.987 回答
0

当您从 Map 中获取对象时,您不会获得该对象的副本,而是获得对该对象的引用。这意味着如果您更改对象的属性,则“在”哈希图中的对象也将更改其属性(因为那是同一个对象)。

这是您的代码发生的情况:

  1. 您获得对会话对象“getpassDTO”的引用
  2. 您正在获取该对象的属性的引用(copyProperty 复制对象的引用,它不会克隆您的对象)
  3. 您正在修改这些引用的属性。但是您的“getPassDTO”会话对象仍然引用相同的属性。
于 2013-07-16T09:43:09.423 回答