我正在使用 Java 1.4 和 Log4J。

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

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

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

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

我应该使用

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

反而?

有帮助吗?

解决方案

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

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

其他提示

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

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

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

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

javadoc 为 可串行化 有关于此方法的信息。

您的实现将类似于:

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

如果您不这样做,则反序列化对象后日志将为空。

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

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

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

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

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

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

如果您希望记录器是针对每个实例的,那么是的,如果您要序列化对象,您会希望将其设为瞬态。Log4J 记录器不可序列化,无论如何,在我使用的 Log4J 版本中都不可序列化,因此,如果您不使记录器字段瞬态,您将在序列化时遇到异常。

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

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

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top