java - example - ¿Debo declarar el ObjectMapper de Jackson como un campo estático?
objectmapper java (5)
La clase ObjectMapper
la biblioteca de Jackson parece ser segura para subprocesos .
¿Significa esto que debo declarar mi ObjectMapper
como un campo estático como este?
class Me {
private static final ObjectMapper mapper = new ObjectMapper();
}
En lugar de como un campo de nivel de instancia como este?
class Me {
private final ObjectMapper mapper = new ObjectMapper();
}
A pesar de que ObjectMapper es seguro para subprocesos, no me gustaría declararlo como una variable estática, especialmente en aplicaciones multihilo. Ni siquiera porque sea una mala práctica, sino porque corre un gran riesgo de interbloqueo. Lo estoy contando desde mi propia experiencia. Creé una aplicación con 4 subprocesos idénticos que estaban obteniendo y procesando datos JSON de servicios web. Mi aplicación se detenía con frecuencia en el siguiente comando, de acuerdo con el volcado del hilo:
Map aPage = mapper.readValue(reader, Map.class);
Además de eso, el rendimiento no fue bueno. Cuando reemplacé la variable estática con la variable basada en instancia, el bloqueo desapareció y el rendimiento se cuadruplicó. Es decir, 2.4 millones de documentos JSON fueron procesados en 40min.56seg., En lugar de 2.5 horas antes.
Aunque es seguro declarar un ObjectMapper estático en términos de seguridad de subprocesos, debe tener en cuenta que la construcción de variables de objetos estáticos en Java se considera una mala práctica. Para más detalles, vea ¿Por qué las variables estáticas se consideran malas? (y si quieres, mi respuesta )
En resumen, debe evitarse lo estático porque dificulta la escritura de pruebas unitarias concisas. Por ejemplo, con un ObjectMapper final estático, no puede intercambiar la serialización JSON por un código ficticio o un no-op.
Además, una final estática le impide volver a configurar ObjectMapper en tiempo de ejecución. Puede que no imagine una razón para eso ahora, pero si se encierra en un patrón final estático, nada menos que derribar el cargador de clases le permitirá reiniciarlo.
En el caso de ObjectMapper está bien, pero en general es una mala práctica y no hay ninguna ventaja sobre el uso de un patrón único o inversión de control para administrar sus objetos de larga duración.
Sí, eso es seguro y recomendable.
La única advertencia de la página a la que hizo referencia es que no puede modificar la configuración del asignador una vez que se comparte; pero no está cambiando la configuración, así que está bien. Si fuera necesario cambiar la configuración, lo haría desde el bloque estático y también estaría bien.
EDITAR : (2013/10)
Con 2.0 y superior, lo anterior se puede aumentar al observar que hay una forma aún mejor: usar los objetos ObjectWriter
y ObjectReader
, que pueden ser construidos por ObjectMapper
. Son totalmente inmutables, seguros para subprocesos, lo que significa que en teoría no es posible causar problemas de seguridad de subprocesos (lo que puede ocurrir con ObjectMapper
si el código intenta reconfigurar la instancia).
Un truco que aprendí de este PR si no quiere definirlo como una variable final estática, pero quiere ahorrar un poco de sobrecarga y garantizar la seguridad de subprocesos.
private static final ThreadLocal<ObjectMapper> om = new ThreadLocal<ObjectMapper>() {
@Override
protected ObjectMapper initialValue() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return objectMapper;
}
};
public static ObjectMapper getObjectMapper() {
return om.get();
}
Crédito al autor.
com.fasterxml.jackson.databind.type.TypeFactory._hashMapSuperInterfaceChain (HierarchicType)
com.fasterxml.jackson.databind.type.TypeFactory._findSuperInterfaceChain(Type, Class)
com.fasterxml.jackson.databind.type.TypeFactory._findSuperTypeChain(Class, Class)
com.fasterxml.jackson.databind.type.TypeFactory.findTypeParameters(Class, Class, TypeBindings)
com.fasterxml.jackson.databind.type.TypeFactory.findTypeParameters(JavaType, Class)
com.fasterxml.jackson.databind.type.TypeFactory._fromParamType(ParameterizedType, TypeBindings)
com.fasterxml.jackson.databind.type.TypeFactory._constructType(Type, TypeBindings)
com.fasterxml.jackson.databind.type.TypeFactory.constructType(TypeReference)
com.fasterxml.jackson.databind.ObjectMapper.convertValue(Object, TypeReference)
El método _hashMapSuperInterfaceChain en la clase com.fasterxml.jackson.databind.type.TypeFactory está sincronizado. Estoy viendo la contención en el mismo a altas cargas.
Puede ser otra razón para evitar un ObjectMapper estático.