Log4J记录器是否应声明为瞬态?
我将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());
反而?
解决方案
回答
记录器必须是静态的;这将使其不可序列化。
没有必要使记录器为非静态,除非我们有充分的理由这样做。
回答
如何使用静态记录器?还是我们需要为该类的每个实例使用不同的记录器参考?静态字段默认情况下不序列化;我们可以显式声明字段以使用名为serialPersistentFields
的ObjectStreamField的私有,静态最终数组进行序列化。请参阅Oracle文档
新增内容:
使用getLogger(getClass())时,将在每个实例中使用相同的记录器。如果要为每个实例使用单独的记录器,则必须在getLogger()方法中区分记录器的名称。例如getLogger(getClass()。getName()+ hashCode())。然后,我们应该使用transient属性来确保记录器未序列化。
回答
尝试改为使Logger静态。比我们不必在意的是串行化,因为它是由类加载器处理的。
回答
如果希望Logger按实例进行,则可以,如果要序列化对象,则希望使其成为瞬态。 Log4J Logger不能序列化,也不是我正在使用的Log4J版本,因此,如果不将Logger字段设为瞬态,则会在序列化中遇到异常。
回答
记录器不可序列化,因此在将它们存储在实例字段中时必须使用瞬态。
如果要在反序列化之后恢复记录器,则可以存储级别(字符串)表示对象(该对象确实已序列化)。
回答
通常,最好通过线程本地状态来处理这类情况,尤其是在EJB中。通常,用例就像我们遇到问题的特定事务一样,我们需要提升日志记录以对该操作进行调试,以便可以生成有关问题操作的详细日志记录。在事务中携带一些线程本地状态,并使用它来选择正确的记录器。坦率地说,我不知道在这种环境中在INSTANCE上设置级别会在什么地方有利,因为实例到事务的映射应该是容器级别的函数,我们实际上将无法控制在实例中使用哪个实例。无论如何给定交易。
即使在处理DTO的情况下,通常也不用以给定特定实例为必需的方式来设计系统,因为设计很容易演变为错误的选择。我们可能需要一个月的时间,然后再决定效率方面的考虑(缓存或者其他生命周期更改优化)将打破我们对将实例映射到工作单元的假设。
回答
可以将记录器字段声明为静态或者瞬态。
两种方式都确保writeObject()方法在序列化期间不会尝试将字段写入输出流。
通常,记录器字段被声明为静态的,但是如果我们需要将其作为实例字段,则只需将其声明为瞬态即可,因为通常对任何不可序列化的字段都将其声明为静态。反序列化时,logger字段将为null,因此,我们必须实现readObject()方法来正确初始化它。
回答
如果我们真的想使用瞬态方法,则需要在对对象进行反序列化时重置日志。做到这一点的方法是实现该方法:
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。
回答
有充分的理由使用实例记录器。一个非常好的用例是,我们可以在超类中声明记录器,并将其在所有子类中使用(唯一的缺点是,超类的日志属于该子类,但通常很容易看到)。
(就像其他人提到的使用静态或者瞬态)。