重新部署项目后,存储在会话中的对象将不匹配 class

Object stored in session will not match same class after redeploy of project

我有两个 class 扩展 WaterBodyStreamLakeStreamLake 存储在 Apache Shiro 1.2.4 管理的会话中,以备后用。

WaterBody lake = new Lake();
session.setAttribute("water", lake);`

并使用

检索它
WaterBody water = (WaterBody) session.getAttribute("water");

在重新启动 Wildfly 9.0.2 服务器时,我可以将对象存储到会话中并检索它们。我是否将 LakeStream 存储到 water 会话属性并不重要。但是,在不重新启动 Wildfly 服务器的情况下重新部署项目后,我可以将与以前相同的 class 类型存储到会话属性 water 即:不能先存储 Lake 然后重新部署商店 Stream。我收到此错误:

 java.lang.ClassCastException: cannot assign instance of [Lfwp.fish.water.HydroUnit; to field fwp.fish.water.WaterBody.hucs of type [Lfwp.fish.water.HydroUnit; in instance of fwp.fish.water.Stream

class WaterBody 具有类型为 HydroUnit 的私有字段的 getter 和 setter,因为这是由 StreamLake 继承的错误没有意义。

如果重新开始,water 会话属性在重新部署之前和之后都分配给了相同的 class,它将把值分配给会话变量就好了,然后在检索它时会抛出这个错误:

java.lang.ClassCastException: fwp.fish.water.Lake cannot be cast to fwp.fish.water.WaterBody

因为 Lake 扩展了 WaterBody 它应该没有问题。

对于 Shiro,我们使用 ehcache 2.9 作为会话缓存。

似乎 serialVersionUID 被缓存忽略了,或者它在幕后生成了一个不同的缓存,并且它认为 classes 在重新部署之间没有变化时已经发生了变化。

class是:

public class WaterBody implements Serializable {
    private static final long serialVersionUID = -5481729151007010163L;
    private int id;
    private HydroUnit[] hucs;

    public WaterBody() {
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public HydroUnit[] getHucs() {
        return hucs;
    }

    public void setHucs(HydroUnit[] hucs) {
        this.hucs = hucs;
    }
}

public class Lake extends WaterBody {
    private static final long serialVersionUID = -925557850476426686L;
    private String centroidLatitude;
    private String centroidLongitude;

    public Lake(){
        super();    
    }

    public String getCentroidLatitude() {
        return centroidLatitude;
    }

    public void setCentroidLatitude(String centroidLatitude) {
        this.centroidLatitude = centroidLatitude;
    }

    public String getCentroidLongitude() {
        return centroidLongitude;
    }

    public void setCentroidLongitude(String centroidLongitude) {
        this.centroidLongitude = centroidLongitude;
    }
}

public class Stream extends WaterBody {
    private static final long serialVersionUID = 2556033593701784053L;
    private String mouthLatitude;
    private String mouthLongitude;

    public Stream(){
         super();       
    }

    public String getMouthLatitude() {
        return mouthLatitude;
    }

    public void setMouthLatitude(String mouthLatitude) {
        this.mouthLatitude = mouthLatitude;
    }

    public String getMouthLongitude() {
        return mouthLongitude;
    }

    public void setMouthLongitude(String mouthLongitude) {
        this.mouthLongitude = mouthLongitude;
    }
}

在研究这个问题之后,我们发现了这个问题。当项目 运行 第一次 ClassLoader 创建 类 时使用它们,然后将对象存储到会话中。在服务器为 运行 时对该项目进行热重新部署后,当它再次加载 Class 时,ClassLoader 会创建该 Class 的新实例。但是,会话仍然记得上次存储 Class 的对象时的 Class,并且有时会让新对象存储在会话中,显示为同一对象。但是在检索对象时,它仍然由 Class 的旧实例定义,因此当试图将其转换为 Class 的当前实例时,它将失败。

我们的解决方案是针对在会话中存储对象的项目,将将作为对象存储在会话中的 类 分离到项目中包含的单独 .jar 模块中被重新部署。这样一来,如果 Class 文件发生更改,服务器将不得不重新启动,因为模块依赖性发生了更改。现在,如果项目是热重新部署的,类 保持不变,因为它们不在正在刷新的 .war 文件中。