Java:序列化外部(最终)字段
Java: serialize external (final) field
我有一个使用 org.jasypt.util.password.StrongPasswordEncryptor
作为其字段之一的 class PasswordEncryptor
,因为我正在尝试使应用 'clusterable' 全部 class es 需要可序列化以进行会话复制,但每当访问 PasswordEncryptor
时,我 运行 都会出现以下异常:
Caused by: java.io.NotSerializableException: org.jasypt.util.password.StrongPasswordEncryptor
at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:891)
at org.jboss.marshalling.river.RiverMarshaller.doWriteFields(RiverMarshaller.java:1063)
at org.jboss.marshalling.river.RiverMarshaller.doWriteSerializableObject(RiverMarshaller.java:1019)
at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:885)
at org.jboss.marshalling.river.RiverMarshaller.doWriteFields(RiverMarshaller.java:1063)
at org.jboss.marshalling.river.RiverMarshaller.doWriteSerializableObject(RiverMarshaller.java:1019)
at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:885)
at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:680)
at org.jboss.marshalling.AbstractObjectOutput.writeObject(AbstractObjectOutput.java:62)
at org.jboss.marshalling.AbstractMarshaller.writeObject(AbstractMarshaller.java:119)
at org.jboss.as.clustering.SimpleMarshalledValue.getBytes(SimpleMarshalledValue.java:74)
at org.jboss.as.clustering.SimpleMarshalledValue.writeObject(SimpleMarshalledValue.java:172)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) [rt.jar:1.6.0_34]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) [rt.jar:1.6.0_34]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) [rt.jar:1.6.0_34]
at java.lang.reflect.Method.invoke(Method.java:622) [rt.jar:1.6.0_34]
at org.jboss.marshalling.reflect.SerializableClass.callWriteObject(SerializableClass.java:175)
at org.jboss.marshalling.river.RiverMarshaller.doWriteSerializableObject(RiverMarshaller.java:1007)
at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:885)
at org.jboss.marshalling.AbstractObjectOutput.writeObject(AbstractObjectOutput.java:62)
at org.jboss.marshalling.AbstractMarshaller.writeObject(AbstractMarshaller.java:119)
at org.infinispan.marshall.MarshallUtil.marshallMap(MarshallUtil.java:60)
at org.infinispan.marshall.exts.MapExternalizer.writeObject(MapExternalizer.java:63)
at org.infinispan.marshall.exts.MapExternalizer.writeObject(MapExternalizer.java:47)
at org.infinispan.marshall.jboss.ExternalizerTable$ExternalizerAdapter.writeObject(ExternalizerTable.java:406)
at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:145)
at org.jboss.marshalling.AbstractObjectOutput.writeObject(AbstractObjectOutput.java:62)
at org.jboss.marshalling.AbstractMarshaller.writeObject(AbstractMarshaller.java:119)
at org.infinispan.atomic.AtomicHashMap$Externalizer.writeObject(AtomicHashMap.java:229)
at org.infinispan.atomic.AtomicHashMap$Externalizer.writeObject(AtomicHashMap.java:226)
at org.infinispan.marshall.jboss.ExternalizerTable$ExternalizerAdapter.writeObject(ExternalizerTable.java:406)
at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:145)
at org.jboss.marshalling.AbstractObjectOutput.writeObject(AbstractObjectOutput.java:62)
at org.jboss.marshalling.AbstractMarshaller.writeObject(AbstractMarshaller.java:119)
at org.infinispan.marshall.jboss.AbstractJBossMarshaller.objectToObjectStream(AbstractJBossMarshaller.java:86)
at org.infinispan.marshall.VersionAwareMarshaller.objectToObjectStream(VersionAwareMarshaller.java:151)
at org.infinispan.marshall.AbstractDelegatingMarshaller.objectToObjectStream(AbstractDelegatingMarshaller.java:44)
at org.infinispan.marshall.MarshalledValue.serialize0(MarshalledValue.java:119)
... 117 more
Caused by: an exception which occurred:
in field spe
in field bean
in object java.util.HashMap@b629b463
in object org.jboss.as.clustering.SimpleMarshalledValue@b629b463
in object org.infinispan.util.FastCopyHashMap@43ad73a2
in object org.infinispan.atomic.AtomicHashMap@4fd181fe
in object org.infinispan.marshall.MarshalledValue@4fd181fe
in object org.infinispan.commands.write.PutKeyValueCommand@ce32d716
in object org.infinispan.commands.tx.PrepareCommand@293098b
我已经尝试将字段标记为 transient
,如下所示:
import java.io.Serializable;
import org.jasypt.util.password.StrongPasswordEncryptor;
public class PasswordEncryptor implements Serializable {
private static final long serialVersionUID = 1L;
//Need to mark transient as its not serializable
private transient StrongPasswordEncryptor spe = new StrongPasswordEncryptor();
public String encrypt(String password){
return spe.encryptPassword(password);
}
public boolean isPasswordCorrect(String enteredPassword, String passwordHash){
return spe.checkPassword(enteredPassword, passwordHash);
}
}
而且我不能使用包装器来子class StrongPasswordEncryptor 因为它是最终的
还有其他方法吗? (最好不引入任何其他库)
您需要将字段标记为 transient
,就像您已经完成的那样。然后你必须手动序列化 class 的单个属性。这看起来像这样:
private void writeObject(ObjectOutputStream os){
try{
os.defaultWriteObject();
os.writeChars(spe.encryptPassword("your password"));
}
catch (Exception e){
e.printStackTrace();
}
}
由于您正在使用零参数构造函数创建 StrongPasswordEncryptor,因此您不需要任何其他数据来创建它。它没有你需要保存的状态,所以根本不需要序列化它。在反序列化后简单地重新创建它是安全的。
标记它 transient
是正确的做法。
但是,反序列化对象中的瞬态字段为空。
您可以提供 readObject
方法,如 the Serializable documentation:
中所述
private transient StrongPasswordEncryptor spe = new StrongPasswordEncryptor();
/**
* Automatically called when this object is deserialized.
*/
private void readObject(ObjectInputStream in)
throws IOException,
ClassNotFoundException {
in.defaultReadObject();
spe = new StrongPasswordEncryptor();
}
另一种选择是使字段成为瞬态并延迟初始化它:
private transient StrongPasswordEncryptor spe;
/**
* Returns this object's StrongPasswordEncryptor, creating it
* if necessary.
*/
private StrongPasswordEncryptor getEncryptor() {
if (spe == null) {
spe = new StrongPasswordEncryptor();
}
return spe;
}
public String encrypt(String password){
return getEncryptor().encryptPassword(password);
}
public boolean isPasswordCorrect(String enteredPassword, String passwordHash){
return getEncryptor().checkPassword(enteredPassword, passwordHash);
}
从 Infinispan 的角度来看,控制 classes 序列化方式的最佳方式,特别是当 classes 是最终的或者您无法修改 class 时,是为您要序列化的 class 提供您自己的 Infinispan Externalizer。 Infinispan 的用户指南包含关于 how to Plug Infinispan with User-Defined Externalizers explaining its benefits, which include reduced payloads, faster serialization and gets around issues when classes to be serialized cannot be modified. Apart from the examples in the documentation, the Infinispan source code (ASL2) 的完整章节,包含大量 Externalizer 示例来实现您的需求。
更具体地说,PasswordEncryptor 的 Externalizer 不需要在 writeObject
中编写任何内容,而在 readObject
中它只需要实例化 PasswordEncryptor。
我有一个使用 org.jasypt.util.password.StrongPasswordEncryptor
作为其字段之一的 class PasswordEncryptor
,因为我正在尝试使应用 'clusterable' 全部 class es 需要可序列化以进行会话复制,但每当访问 PasswordEncryptor
时,我 运行 都会出现以下异常:
Caused by: java.io.NotSerializableException: org.jasypt.util.password.StrongPasswordEncryptor
at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:891)
at org.jboss.marshalling.river.RiverMarshaller.doWriteFields(RiverMarshaller.java:1063)
at org.jboss.marshalling.river.RiverMarshaller.doWriteSerializableObject(RiverMarshaller.java:1019)
at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:885)
at org.jboss.marshalling.river.RiverMarshaller.doWriteFields(RiverMarshaller.java:1063)
at org.jboss.marshalling.river.RiverMarshaller.doWriteSerializableObject(RiverMarshaller.java:1019)
at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:885)
at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:680)
at org.jboss.marshalling.AbstractObjectOutput.writeObject(AbstractObjectOutput.java:62)
at org.jboss.marshalling.AbstractMarshaller.writeObject(AbstractMarshaller.java:119)
at org.jboss.as.clustering.SimpleMarshalledValue.getBytes(SimpleMarshalledValue.java:74)
at org.jboss.as.clustering.SimpleMarshalledValue.writeObject(SimpleMarshalledValue.java:172)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) [rt.jar:1.6.0_34]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) [rt.jar:1.6.0_34]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) [rt.jar:1.6.0_34]
at java.lang.reflect.Method.invoke(Method.java:622) [rt.jar:1.6.0_34]
at org.jboss.marshalling.reflect.SerializableClass.callWriteObject(SerializableClass.java:175)
at org.jboss.marshalling.river.RiverMarshaller.doWriteSerializableObject(RiverMarshaller.java:1007)
at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:885)
at org.jboss.marshalling.AbstractObjectOutput.writeObject(AbstractObjectOutput.java:62)
at org.jboss.marshalling.AbstractMarshaller.writeObject(AbstractMarshaller.java:119)
at org.infinispan.marshall.MarshallUtil.marshallMap(MarshallUtil.java:60)
at org.infinispan.marshall.exts.MapExternalizer.writeObject(MapExternalizer.java:63)
at org.infinispan.marshall.exts.MapExternalizer.writeObject(MapExternalizer.java:47)
at org.infinispan.marshall.jboss.ExternalizerTable$ExternalizerAdapter.writeObject(ExternalizerTable.java:406)
at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:145)
at org.jboss.marshalling.AbstractObjectOutput.writeObject(AbstractObjectOutput.java:62)
at org.jboss.marshalling.AbstractMarshaller.writeObject(AbstractMarshaller.java:119)
at org.infinispan.atomic.AtomicHashMap$Externalizer.writeObject(AtomicHashMap.java:229)
at org.infinispan.atomic.AtomicHashMap$Externalizer.writeObject(AtomicHashMap.java:226)
at org.infinispan.marshall.jboss.ExternalizerTable$ExternalizerAdapter.writeObject(ExternalizerTable.java:406)
at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:145)
at org.jboss.marshalling.AbstractObjectOutput.writeObject(AbstractObjectOutput.java:62)
at org.jboss.marshalling.AbstractMarshaller.writeObject(AbstractMarshaller.java:119)
at org.infinispan.marshall.jboss.AbstractJBossMarshaller.objectToObjectStream(AbstractJBossMarshaller.java:86)
at org.infinispan.marshall.VersionAwareMarshaller.objectToObjectStream(VersionAwareMarshaller.java:151)
at org.infinispan.marshall.AbstractDelegatingMarshaller.objectToObjectStream(AbstractDelegatingMarshaller.java:44)
at org.infinispan.marshall.MarshalledValue.serialize0(MarshalledValue.java:119)
... 117 more
Caused by: an exception which occurred:
in field spe
in field bean
in object java.util.HashMap@b629b463
in object org.jboss.as.clustering.SimpleMarshalledValue@b629b463
in object org.infinispan.util.FastCopyHashMap@43ad73a2
in object org.infinispan.atomic.AtomicHashMap@4fd181fe
in object org.infinispan.marshall.MarshalledValue@4fd181fe
in object org.infinispan.commands.write.PutKeyValueCommand@ce32d716
in object org.infinispan.commands.tx.PrepareCommand@293098b
我已经尝试将字段标记为 transient
,如下所示:
import java.io.Serializable;
import org.jasypt.util.password.StrongPasswordEncryptor;
public class PasswordEncryptor implements Serializable {
private static final long serialVersionUID = 1L;
//Need to mark transient as its not serializable
private transient StrongPasswordEncryptor spe = new StrongPasswordEncryptor();
public String encrypt(String password){
return spe.encryptPassword(password);
}
public boolean isPasswordCorrect(String enteredPassword, String passwordHash){
return spe.checkPassword(enteredPassword, passwordHash);
}
}
而且我不能使用包装器来子class StrongPasswordEncryptor 因为它是最终的
还有其他方法吗? (最好不引入任何其他库)
您需要将字段标记为 transient
,就像您已经完成的那样。然后你必须手动序列化 class 的单个属性。这看起来像这样:
private void writeObject(ObjectOutputStream os){
try{
os.defaultWriteObject();
os.writeChars(spe.encryptPassword("your password"));
}
catch (Exception e){
e.printStackTrace();
}
}
由于您正在使用零参数构造函数创建 StrongPasswordEncryptor,因此您不需要任何其他数据来创建它。它没有你需要保存的状态,所以根本不需要序列化它。在反序列化后简单地重新创建它是安全的。
标记它 transient
是正确的做法。
但是,反序列化对象中的瞬态字段为空。
您可以提供 readObject
方法,如 the Serializable documentation:
private transient StrongPasswordEncryptor spe = new StrongPasswordEncryptor();
/**
* Automatically called when this object is deserialized.
*/
private void readObject(ObjectInputStream in)
throws IOException,
ClassNotFoundException {
in.defaultReadObject();
spe = new StrongPasswordEncryptor();
}
另一种选择是使字段成为瞬态并延迟初始化它:
private transient StrongPasswordEncryptor spe;
/**
* Returns this object's StrongPasswordEncryptor, creating it
* if necessary.
*/
private StrongPasswordEncryptor getEncryptor() {
if (spe == null) {
spe = new StrongPasswordEncryptor();
}
return spe;
}
public String encrypt(String password){
return getEncryptor().encryptPassword(password);
}
public boolean isPasswordCorrect(String enteredPassword, String passwordHash){
return getEncryptor().checkPassword(enteredPassword, passwordHash);
}
从 Infinispan 的角度来看,控制 classes 序列化方式的最佳方式,特别是当 classes 是最终的或者您无法修改 class 时,是为您要序列化的 class 提供您自己的 Infinispan Externalizer。 Infinispan 的用户指南包含关于 how to Plug Infinispan with User-Defined Externalizers explaining its benefits, which include reduced payloads, faster serialization and gets around issues when classes to be serialized cannot be modified. Apart from the examples in the documentation, the Infinispan source code (ASL2) 的完整章节,包含大量 Externalizer 示例来实现您的需求。
更具体地说,PasswordEncryptor 的 Externalizer 不需要在 writeObject
中编写任何内容,而在 readObject
中它只需要实例化 PasswordEncryptor。