我的java项目中有数千个类。其中一些实现了可序列化的接口。现在这是一个问题。有人可以进入一个类,添加既不是瞬态也不可序列化的新变量。代码编译得很好,但是流程会在运行时爆炸。
为了说明这个
class Foo implements Serializable { .... // all good }
class Foo implements Serializable
{
// OOps, executorService is not serializable. It's not declared as transient either
private ExecutorService executorService = ..
}
我正在考虑编写一个单元测试,该测试将通过所有类并确保“真正的可序列化”。我已经阅读了一些关于连续特定对象的讨论。我理解这个过程但需要
1)创建一个对象
2)序列化然后
3)反序列化。
是否有更有效和实用的方法。也许用反射。通过所有类,如果类具有可序列化,那么所有属性必须是可序列化的或具有临时关键字..
想法?
答案 0 :(得分:2)
1)创建一个对象。 2)序列化然后3)反序列化。
此列表不完整;你还需要初始化。考虑一下这个例子:
class CanBeSerialized implements Serializable {
private String a; // serializable
private Thread t; // not serializable
}
class CannotBeSerialized implements Serializable {
private String a; // serializable
private Thread t = new Thread(); // not serializable
}
您可以序列化和反序列化第一个,但第二个会得到NotSerializableException
。为了进一步复杂化,如果使用接口,你永远无法判断一个类是否会通过序列化,因为它是流接口的类的具体对象:
class PerhapsCanBeSerializedButYouNeverKnow implements Serializable {
private Runnable r; // interface type - who knows?
}
前提是您可以保证以下您的课程所用的所有课程和类进行测试:
然后您可以通过反射自动创建和初始化它们,然后测试序列化。但那是一个非常艰难的条件,不是吗?否则,正确的初始化归结为手动工作。
您可以以不同的方式使用反射:遍历要检查的Class
个对象列表,为它们获取Field[]
,并验证它们是否是瞬态的({{1}或者,如果他们直接(Field.getModifiers()
)或间接(通过超级接口或类)实现Serializable
。另外,请考虑要检查的深度,具体取决于序列化机制的工作深度。
正如Ryan正确指出的那样,如果代码足够邪恶,这种静态序列化检查会失败:
Field.getType().getInterfaces()
或者只是class SeeminglySerializable implements Serializable {
// ...
private void writeObject/readObject() {
throw new NotSerializableException();
}
}
被严重执行。要测试这类问题,您需要实际测试序列化过程,而不是它背后的代码。
答案 1 :(得分:1)
如果序列化是您应用的关键部分,那么请在测试中包含序列化。类似的东西:
@Test
public void aFooSerializesAndDeserializesCorrectly {
Foo fooBeforeSerialization = new Foo();
ReflectionUtils.randomlyPopulateFields(foo);
Foo fooAfterSerialization = Serializer.serializeAndDeserialize(foo);
assertThat(fooAfterSerialization, hasSameFieldValues(fooBeforeSerialization));
}
修改:randomlyPopulateFields
的简单实现:
public static void randomlyPopulateFields(final Object o) {
ReflectionUtils.doWithFields(o.getClass(), new ReflectionUtils.FieldCallback() {
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
ReflectionUtils.makeAccessible(field);
ReflectionUtils.setField(field, o, randomValueFor(field.getType()));
}
private Random r = new Random();
private Object randomValueFor(Class<?> type) {
if (type == String.class) {
return String.valueOf(r.nextDouble());
} else if (type == Boolean.class || type == Boolean.TYPE) {
return r.nextBoolean();
} else if (type == Byte.class || type == Byte.TYPE) {
return (byte) r.nextInt();
} else if (type == Short.class || type == Short.TYPE) {
return (short) r.nextInt();
} else if (type == Integer.class || type == Integer.TYPE) {
return r.nextInt();
} else if (type == Long.class || type == Long.TYPE) {
return (long) r.nextInt();
} else if (Number.class.isAssignableFrom(type) || type.isPrimitive()) {
return Byte.valueOf("1234");
} else if (Date.class.isAssignableFrom(type)) {
return new Date(r.nextLong());
} else {
System.out.println("Sorry, I don't know how to generate values of type " + type);
return null;
}
}
});
}