31

我正在使用带有 Log4J 的 Java 1.4。

我的一些代码涉及序列化和反序列化值对象 (POJO)。

我的每个 POJO 都声明了一个记录器

private final Logger log = Logger.getLogger(getClass());

序列化程序抱怨 org.apache.log4j.Logger 不是可序列化的。

我应该使用

private final transient Logger log = Logger.getLogger(getClass());

反而?

4

9 回答 9

27

使用静态记录器怎么样?或者您是否需要为每个类实例使用不同的记录器引用?静态字段默认不序列化;您可以显式声明字段以使用名为的私有、静态、最终数组进行序列ObjectStreamFieldserialPersistentFields请参阅 Oracle 文档

添加内容:当您使用getLogger(getClass())时,您将在每个实例中使用相同的记录器。如果要为每个实例使用单独的记录器,则必须在 getLogger() 方法中区分记录器的名称。例如 getLogger(getClass().getName() + hashCode())。然后,您应该使用瞬态属性来确保记录器未序列化。

于 2008-09-17T11:20:52.340 回答
11

记录器必须是静态的;这将使其不可序列化。

没有理由让记录器成为非静态的,除非你有充分的理由这样做。

于 2008-09-17T11:20:25.883 回答
9

如果您真的想采用瞬态方法,则需要在反序列化对象时重置日志。这样做的方法是实现该方法:

 private void readObject(java.io.ObjectInputStream in) 
   throws IOException, ClassNotFoundException;

Serializable的 javadocs包含有关此方法的信息。

您的实现将类似于:

 private void readObject(java.io.ObjectInputStream in) 
     throws IOException, ClassNotFoundException {
   log = Logger.getLogger(...);
   in.defaultReadObject();
 }

如果你不这样做,那么在反序列化你的对象后 log 将为空。

于 2008-09-17T12:27:37.923 回答
5

将您的记录器字段声明为静态或瞬态。

这两种方式都确保 writeObject() 方法在序列化期间不会尝试将字段写入输出流。

通常记录器字段被声明为静态的,但如果您需要它是一个实例字段,只需将其声明为瞬态,就像它通常对任何不可序列化的字段所做的那样。但是,在反序列化时,记录器字段将为空,因此您必须实现一个 readObject() 方法来正确初始化它。

于 2008-09-17T12:26:37.113 回答
2

尝试将 Logger 设为静态。比你不必关心序列化,因为它是由类加载器处理的。

于 2008-09-17T11:21:36.940 回答
2

这类情况,尤其是在 EJB 中,通常最好通过线程本地状态来处理。通常,用例就像您有一个遇到问题的特定事务,您需要提升日志记录以调试该操作,以便您可以生成有关问题操作的详细日志记录。在事务中携带一些线程本地状态并使用它来选择正确的记录器。坦率地说,我不知道在这种环境中在实例上设置级别有什么好处,因为实例到事务的映射应该是容器级别的函数,您实际上无法控制在一个实例中使用哪个实例无论如何给定交易。

即使在处理 DTO 的情况下,以需要给定特定实例的方式设计系统通常也不是一个好主意,因为设计很容易演变为使该选择成为错误选择的方式。从现在开始一个月后,您可能会决定效率考虑(缓存或其他一些改变生命周期的优化)将打破您关于将实例映射到工作单元的假设。

于 2008-09-17T12:26:34.023 回答
0

如果您希望 Logger 是每个实例的,那么是的,如果您要序列化您的对象,您可能希望它是瞬态的。Log4J Loggers 不是可序列化的,在我使用的 Log4J 版本中也不是,所以如果你不让你的 Logger 字段是瞬态的,你会在序列化时遇到异常。

于 2008-09-17T11:48:22.837 回答
0

记录器不可序列化,因此在将它们存储在实例字段中时必须使用瞬态。如果您想在反序列化后恢复记录器,您可以将级别(字符串)存储在您的对象中,该对象确实会被序列化。

于 2008-09-17T11:54:18.283 回答
0

使用实例记录器有充分的理由。一个非常好的用例是,您可以在超类中声明记录器并在所有子类中使用它(唯一的缺点是来自超类的日志归于子类,但通常很容易看到那个)。

(就像其他人提到的使用静态或瞬态一样)。

于 2008-09-17T15:02:56.067 回答