我正在使用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());
代替?
答案 0 :(得分:26)
使用静态记录器怎么样?或者您是否需要为每个类的实例提供不同的记录器参考?默认情况下,静态字段未序列化;您可以使用名为ObjectStreamField
的{{1}}私有静态最终数组显式声明要序列化的字段。 See Oracle documentation
添加内容: 当您使用getLogger(getClass())时,您将在每个实例中使用相同的记录器。如果要为每个实例使用单独的记录器,则必须在getLogger() - 方法中区分记录器的名称。例如getLogger(getClass()。getName()+ hashCode())。然后,您应该使用transient属性来确保记录器未序列化。
答案 1 :(得分:11)
记录器必须是静态的;这会使它不可序列化。
没有理由让logger非静态,除非你有充分的理由这样做。
答案 2 :(得分: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将为null。
答案 3 :(得分:5)
将您的记录器字段声明为静态或瞬态。
两种方式都确保writeObject()方法在序列化期间不会尝试将字段写入输出流。
通常记录器字段被声明为静态,但如果您需要它作为实例字段,则只需将其声明为瞬态,因为它通常用于任何非可序列化字段。在反序列化时,logger字段将为null,因此您必须实现readObject()方法以正确初始化它。
答案 4 :(得分:2)
尝试将Logger设为静态。你不必关心序列化,因为它是由类加载器处理的。
答案 5 :(得分:2)
这些情况,特别是在EJB中,通常最好通过线程本地状态来处理。通常,用例就像您遇到一个特定的事务而遇到问题,并且您需要将日志记录提升为该操作的调试,以便您可以生成有关问题操作的详细日志记录。在整个事务中携带一些线程本地状态,并使用它来选择正确的记录器。坦率地说,我不知道在这个环境中在INSTANCE上设置级别会有什么好处,因为实例到事务的映射应该是一个容器级别的函数,你实际上无法控制在一个实例中使用哪个实例。无论如何都要进行交易。
即使在你正在处理DTO的情况下,通常也不应该以需要给定特定实例的方式设计系统,因为设计很容易以一种糟糕的选择方式发展。您可以从现在开始一个月,并确定效率考虑因素(缓存或其他一些生命周期更改优化)将打破您关于将实例映射到工作单元的假设。
答案 6 :(得分:0)
如果您希望Logger是按实例进行的,那么您可能希望在要序列化对象时使其成为瞬态。 Log4J记录器不是可序列化的,不是我正在使用的Log4J版本,所以如果你没有使你的Logger字段成为瞬态,你将获得序列化的例外。
答案 7 :(得分:0)
记录器不可序列化,因此在将它们存储在实例字段中时必须使用瞬态。 如果要在反序列化后恢复记录器,可以在对象中存储Level(String),并将其序列化。
答案 8 :(得分:0)
使用实例记录器有充分的理由。一个非常好的用例是你可以在超类中声明记录器并在所有子类中使用它(唯一的缺点是来自超类的日志归因于子类,但它通常很容易看到了。)
(像其他人提到的那样使用静态或瞬态)。