Как правильно использовать Logger в классе Serializable Java? - PullRequest
16 голосов
/ 11 мая 2010

У меня есть следующий ( doctored ) класс в системе, над которой я работаю, и Findbugs генерирует предупреждение SE_BAD_FIELD , и я пытаюсь Я понимаю, почему это будет сказано, прежде чем я исправлю это так, как я думал. Причина, по которой я запутался, заключается в том, что описание, по-видимому, указывает на то, что я не использовал никаких других не сериализуемых полей экземпляра в классе, но bar.model.Foo также не сериализуем и используется точно так же (насколько я могу сказать) но Findbugs не выдает для него предупреждения.

import bar.model.Foo;

import java.io.File;
import java.io.Serializable;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Demo implements Serializable {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    private final File file;
    private final List<Foo> originalFoos;
    private Integer count;
    private int primitive = 0;

    public Demo() {
        for (Foo foo : originalFoos) {
            this.logger.debug(...);
        }
    }

    ...

}

Мой первоначальный взгляд на решение состоит в том, чтобы получить ссылку на регистратор с завода, как я его использую:

public DispositionFile() {
    Logger logger = LoggerFactory.getLogger(this.getClass());
    for (Foo foo : originalFoos) {
        this.logger.debug(...);
    }
}

Хотя это не кажется особенно эффективным.

Мысли

Ответы [ 4 ]

19 голосов
/ 11 мая 2010

Во-первых, не оптимизируйте преждевременно. Может случиться так, что LoggerFactory.getLogger() достаточно быстр и не вносит значительных накладных расходов во время выполнения. Если сомневаетесь, профилируйте его.

Во-вторых, причина того, что findbugs не жалуется на использование Foo, заключается в том, что у класса нет поля типа Foo, у него есть поле типа List. Обобщения стираются во время компиляции, в классе нет фактической ссылки на Foo, что касается определения поля. Во время выполнения тот факт, что Foo не является сериализуемым, вызовет исключение, если вы попытаетесь сериализовать экземпляр класса Demo, но findbugs не может этого знать.

Моей первой реакцией было бы сделать Logger статическим полем, а не полем экземпляра. Должно работать нормально в этой ситуации.

public class Demo implements Serializable {
   private static final Logger logger = LoggerFactory.getLogger(Demo.class);

   // .. other stuff
}
7 голосов
/ 11 мая 2010

Я не хочу, чтобы все происходило по касательной, но рассматривали ли вы обычную инициализацию логгеров?

private static final Logger logger = LoggerFactory.getLogger(Demo.class);

Если вам не нужны разные регистраторы для каждого экземпляра (что необычно), проблема исчезнет.

Кстати, автор из SL4J сказал (в критике оберток Log4J, таких как регистрация общего достояния),

Чаще всего эти обертки имеют сомнительное качество, так что стоимость неактивной (или отключенной) регистрации заявления умножается на коэффициент 1 000 (одна тысяча) по сравнению с прямое использование log4j. Самый распространенный ошибка в классах-оболочках вызов Logger.getLogger метод для каждого запроса журнала. Это гарантированно нанесет ущерб вашему производительность приложения. Действительно !!!

Это говорит о том, что ваша альтернативная идея получать регистратор каждый раз, когда вам это нужно, не рекомендуется.

6 голосов
/ 12 мая 2010

FindBugs вводит вас в заблуждение в данном конкретном случае, потому что интерфейс org.slf4j.Logger не помечен как java.io.Serializable. Однако реализации регистратора SLF4J, поставляемые с SLF4J, поддерживают сериализацию «из коробки». Попытайся. Вы увидите, что это работает.

Вот выдержка из FAQ по SLF4j:

В отличие от статических переменных, экземпляр переменные сериализуются по умолчанию. Начиная с версии 1.5.3 SLF4J, регистратор экземпляры переживают сериализацию. Таким образом, Сериализация хоста класса нет больше требует каких-либо специальных действий, даже когда регистраторы объявлены как переменные экземпляра. В предыдущем версии, экземпляры регистратора, необходимые для быть объявленным в качестве временного в хосте класс.

См. Также http://slf4j.org/faq.html#declared_static

3 голосов
/ 12 мая 2010

Моя первоначальная реакция - задуматься, имеет ли смысл сериализовать экземпляр Logger в вашем объекте. Когда вы десериализуете это позже, действительно ли справедливо ожидать, что среда Logger будет правильной? Я думаю, что лучше просто пойти с этим и назвать это день:

private transient Logger logger = LoggerFactory.getLogger(this.getClass());
...