我需要使用RMI为客户发送药物计划。服务器和客户端位于不同的项目中,两者都定义了以下类(实体和远程接口(:
public class Plan implements Serializable {
private Integer id;
private Date periodStart;
private Date periodEnd;
private Integer patientId;
private Medication medications;
}
public class Medication implements Serializable {
private Integer id;
private String name;
private Integer dosage;
private Integer intakeInterval;
}
public interface PillService extends Remote {
public Plan getPlan(int id) throws RemoteException;
}
上面的代码工作正常,但我需要在Plan
中列出药物列表,如下所示:
public class Plan implements Serializable {
...
private List<Medication> medications;
}
如果我使用此Plan
类运行,则会出现以下异常:
Client exception: java.rmi.UnmarshalException: error unmarshalling return; nested exception is:
java.lang.ClassNotFoundException: org.hibernate.collection.internal.PersistentBag (no security manager: RMI class loader disabled)
在此之后,我添加了SecutiryManager
System
出现错误:
java.security.AccessControlException: access denied ("java.net.SocketPermission" "127.0.0.1:1099" "connect,resolve")
at java.security.AccessControlContext.checkPermission(AccessControlContext.java:472)
因此,它无需List<Medication>
即可正常工作Plan
.RMI不喜欢复合对象吗?我是否应该定义一种新的远程方法来单独获取药物清单?
问题似乎是List
的运行时类型。该类在客户端不可用,因此 RMI 正在尝试从远程代码库下载代码。这已被合理地禁用。
最简单的解决方法是使用众所周知的实现进行List
。将列表复制到在Plan
中设置的位置或在自定义writeObject
方法中。
this.medications = new ArrayList<>(medications);
或
private void writeObject(
ObjectOutputStream out
) throws IOException {
this.medications = new ArrayList<>(this.medications);
out.defaultWriteObject();
}
看起来你List
是一个延迟加载实现,所以这将强制它完全加载。您也许可以简单地使用预先加载。
理想情况下,不要使用 RMI,包括 Java 序列化部分。