2010-05-10 21 views
14

Tengo la siguiente (adulterada) clase en un sistema en el que estoy trabajando y Findbugs está generando una advertencia SE_BAD_FIELD y estoy tratando de entender por qué diría eso antes de que lo solucione de la manera en que pensé haría. La razón por la que estoy confundido es porque la descripción parece indicar que no he usado otros campos de instancia no serializables en la clase, pero bar.model.Foo tampoco es serializable y se usa de la misma manera (en la medida en que se puede decir) pero Findbugs no genera ninguna advertencia.¿Cuál es la forma correcta de usar un registrador en una clase Serializable de Java?

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(...); 
     } 
    } 

    ... 

} 

Mi rubor inicial a una solución es conseguir una referencia registrador de la fábrica de la derecha como lo uso:

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

que no parece particularmente eficiente, sin embargo.

¿Pensamientos?

+2

Ceki ha informado de que durante el logback getLogger (...) el método es rápido "suficiente" para ser llamado cuando sea necesario, y no se utiliza solo en un campo estático. –

+0

Eche un vistazo a la clase [Logger] (http://www.jcabi.com/jcabi-log/apidocs-0.7.7/com/jcabi/log/Logger.html) de [jcabi-log] (http://www.jcabi.com/jcabi-log/), que es un contenedor alrededor de SLF4J – yegor256

+0

Lea [respuesta de Ceki] (http://stackoverflow.com/a/2818849/606662) si usa SLF 1.5.3 o posterior. –

Respuesta

18

En primer lugar, no optimice prematuramente. Puede ser que LoggerFactory.getLogger() sea lo suficientemente rápido y no genere una sobrecarga significativa en el tiempo de ejecución. Si tiene dudas, perfúlelo.

En segundo lugar, la razón de que findbugs no se queja sobre el uso de Foo es porque la clase no tiene un campo de tipo Foo, que tiene un campo de tipo List. Los genéricos se borran en el momento de la compilación, no existe una referencia real al Foo en la clase, en lo que respecta a la definición del campo. En tiempo de ejecución, el hecho de que Foo no sea serializable provocaría una excepción si intentara serializar una instancia de la clase Demo, pero findbugs no puede saber esto.

Mi primera reacción sería hacer que el Logger sea un campo estático, en lugar de un campo de instancia. Debería funcionar bien en esta situación.

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

    // .. other stuff 
} 
+0

Nitpick: los Findbugs podrían saber esto: el tipo declarado de un campo (incluyendo cualquier tipo de parámetros/comodines) se escribe en el archivo de clase y, por lo tanto, está disponible para el análisis estático. – meriton

+1

Ah, no, no puede: Findbugs no sabe que una Lista contendrá realmente una referencia no transitoria de tipo T. (Una lista devuelta por Collections.emptyList() no lo hace, por ejemplo). – meriton

+0

¡Mi ofuscación fue demasiado efectiva! :) En la clase real hay muchas referencias a esa lista. Intentaba comprimir por espacio. ¿Cómo cambiaría eso tu respuesta? ¡Gracias por la respuesta hasta ahora! –

7

No quiero que las cosas despeguen en una tangente, pero ¿ha considerado la inicialización convencional de los registradores?

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

Si realmente no necesita diferentes registradores para cada instancia (lo cual es inusual), el problema desaparecería.

Por cierto, el author de SL4J dijo (en una critique de envoltorios Log4J como commons-logging),

Más a menudo que no, estas envolturas son de dudosa calidad tal que el costo de las declaraciones inactivas (o deshabilitadas) de registro se multiplican por un factor de 1'000 (mil) en comparación con uso directo de log4j. El error más común en las clases contenedoras es la invocación del método Logger.getLogger en cada solicitud de registro. Esto es garantizado para causar estragos en el rendimiento de su aplicación . ¡¡¡De Verdad!!!

Eso sugeriría que su idea alternativa de obtener el registrador cada vez que la necesite no es recomendable.

+0

Para ser justos, la siguiente frase de ese artículo dice "Por supuesto, no todos los contenedores son de mala calidad. Por ejemplo, la API de registro de recursos comunes es un excelente ejemplo de una implementación razonable." –

+0

También podría decirse que los registradores siempre deben ser estáticos a menos que necesite un nombre de registrador dinámico o para compartir instancias con subclases o algo similar, así que +1 para eso. –

6

FindBugs le está engañando en este caso particular porque la interfaz org.slf4j.Logger es no marcada como java.io.Serializable. Sin embargo, las implementaciones de registrador SLF4J que se incluyen con SLF4J son compatibles con la serialización lista para usar. Intentalo. Verás que funciona.

Aquí es un extracto de la AYUDA SLF4J:

Contrariamente a las variables estáticas, variables de instancia son serializados por defecto. A partir de SLF4J versión 1.5.3, las instancias del registrador sobreviven a la serialización. Por lo tanto, la serialización de la clase de host no requiere más acción especial, , incluso cuando los registradores se declaran como variables de instancia . En versiones anteriores de , las instancias de registrador necesarias para se declaran como transitorias en la clase de host .

Ver también http://slf4j.org/faq.html#declared_static

+0

Tal vez esté usando una versión anterior a la 1.5.3, en ese caso – skaffman

+1

FindBugs probablemente se confunda porque la interfaz org.slf4j.Logger no está marcada como Serializable incluso en SLF4J versiones 1.5.3 y posteriores. – Ceki

3

Mi reacción inicial es preguntarse si es que tiene sentido para serializar una instancia Logger en su objeto. Cuando lo deserializa más tarde, ¿es realmente justo esperar que el entorno del Registrador sea el correcto? Creo que preferiría ir con esto y lo llaman un día:

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

... siempre que tome medidas para que se vuelva a crear en la deserialización. – EJP

Cuestiones relacionadas