466 Stimmen

Sollte ich Jacksons ObjectMapper als statisches Feld deklarieren?

Die Bibliothek von Jackson ObjectMapper Klasse scheint thread-sicher zu sein .

Bedeutet dies, dass ich meine ObjectMapper als statisches Feld wie folgt

class Me {
    private static final ObjectMapper mapper = new ObjectMapper();
}

statt als Feld auf Instanzebene wie hier?

class Me {
    private final ObjectMapper mapper = new ObjectMapper();
}

625voto

StaxMan Punkte 107669

Ja, das ist sicher und wird empfohlen.

Die einzige Einschränkung auf der Seite, auf die Sie verwiesen haben, ist, dass Sie die Konfiguration des Mappers nicht mehr ändern können, sobald er freigegeben ist; aber da Sie die Konfiguration nicht ändern, ist das in Ordnung. Wenn Sie die Konfiguration ändern müssten, würden Sie das vom statischen Block aus tun, und das wäre auch in Ordnung.

EDITAR : (2013/10)

Ab Version 2.0 gibt es eine noch bessere Möglichkeit, nämlich die Verwendung von ObjectWriter y ObjectReader Objekte, die wie folgt konstruiert werden können ObjectMapper . Sie sind vollständig unveränderlich und thread-sicher, was bedeutet, dass es nicht einmal theoretisch möglich ist, Probleme mit der Thread-Sicherheit zu verursachen (was bei ObjectMapper wenn der Code versucht, die Instanz neu zu konfigurieren).

0 Stimmen

Haben Sie eine Referenzquelle?

38 Stimmen

@StaxMan: Ich bin ein wenig besorgt, wenn ObjectMapper immer noch thread-sicher ist, nachdem ObjectMapper#setDateFormat() genannt wird. Es ist bekannt, dass SimpleDateFormat ist nicht thread-sicher daher ObjectMapper wird nicht sein, es sei denn, es klont z.B. SerializationConfig vor jeder writeValue() (Das bezweifle ich). Können Sie meine Befürchtung entkräften?

68 Stimmen

DateFormat ist tatsächlich unter der Haube geklont. Guter Verdacht dort, aber Sie sind abgedeckt. :)

88voto

Gary Greenberg Punkte 861

Obwohl ObjectMapper thread-sicher ist, würde ich dringend davon abraten, ihn als statische Variable zu deklarieren, insbesondere in einer Multithreading-Anwendung. Nicht einmal, weil es eine schlechte Praxis ist, sondern weil Sie ein hohes Risiko eines Deadlocks eingehen. Ich erzähle das aus eigener Erfahrung. Ich habe eine Anwendung mit 4 identischen Threads erstellt, die JSON-Daten von Webdiensten abrufen und verarbeiten. Laut dem Thread-Dump blieb meine Anwendung häufig bei folgendem Befehl hängen:

Map aPage = mapper.readValue(reader, Map.class);

Abgesehen davon war die Leistung nicht gut. Als ich die statische Variable durch die instanzbasierte Variable ersetzte, verschwand das Stocken und die Leistung vervierfachte sich. D.h. 2,4 Millionen JSON-Dokumente wurden in 40 Minuten und 56 Sekunden verarbeitet, statt vorher 2,5 Stunden.

23 Stimmen

Die Antwort von Gary ist absolut sinnvoll. Aber mit einer Erstellung eines ObjectMapper Instanz für jede Klasseninstanz kann zwar Sperren verhindern, kann aber später die GC sehr belasten (stellen Sie sich eine ObjectMapper-Instanz für jede Instanz der Klasse vor, die Sie erstellen). Ein Mittelweg kann sein, anstatt nur eine (öffentliche) statische ObjectMapper Instanz in der gesamten Anwendung, können Sie eine (private) statisch Instanz von ObjectMapper in jeder Klasse . Dies wird eine globale Sperre (durch die Verteilung der Last klassenweise) zu reduzieren, und wird kein neues Objekt entweder erstellen, daher leicht auf GC zu.

1 Stimmen

Und natürlich ist die Aufrechterhaltung einer ObjectPool ist der beste Weg, den Sie einschlagen können - und damit die beste GC y Lock Leistungen. Unter dem folgenden Link finden Sie die apache-common's ObjectPool Umsetzung. commons.apache.org/proper/commons-pool/api-1.6/org/apache/

2 Stimmen

So sind Aufruf zu ObjectReader Instanzen nicht blockieren d.h. sagen objectReader.readTree in Multithreading-Anwendung aufgerufen wird, Threads werden nicht blockiert warten auf einen anderen Thread, mit Jackson 2.8.x

11voto

Lin_n Punkte 159

Ein Trick, den ich daraus gelernt habe PR wenn Sie sie nicht als statische finale Variable definieren wollen, aber ein wenig Overhead sparen und Threadsicherheit garantieren wollen.

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

Dank an den Autor.

7 Stimmen

Es besteht jedoch die Gefahr eines Speicherlecks, da die ObjectMapper wird an den Thread angehängt, der Teil eines Pools sein kann.

1 Stimmen

@KenstonChoi Sollte kein Problem sein, AFAIU. Die Threads kommen und gehen, die Thread-Locals kommen und gehen mit den Threads. Je nach der Menge der gleichzeitigen Threads können Sie sich den Speicher leisten oder nicht, aber ich sehe keine "Lecks".

4 Stimmen

@IvanBalashov, aber wenn der Thread von/zu einem Thread-Pool (z.B. Container wie Tomcat) erstellt/zurückgegeben wird, bleibt er bestehen. Dies kann in manchen Fällen erwünscht sein, ist aber etwas, dessen wir uns bewusst sein müssen.

3voto

JBCP Punkte 12445

Obwohl es sicher ist, einen statischen ObjectMapper im Hinblick auf die Threadsicherheit zu deklarieren, sollten Sie sich bewusst sein, dass die Konstruktion von statischen Objektvariablen in Java als schlechte Praxis gilt. Für weitere Einzelheiten siehe Warum werden statische Variablen als böse angesehen? (und wenn Sie möchten, meine Antwort )

Kurz gesagt, Statik sollte vermieden werden, weil sie das Schreiben von prägnanten Unit-Tests erschwert. Bei einem statischen finalen ObjectMapper können Sie beispielsweise die JSON-Serialisierung nicht gegen Dummy-Code oder ein No-op austauschen.

Darüber hinaus verhindert ein statisches Finale, dass Sie ObjectMapper zur Laufzeit neu konfigurieren müssen. Vielleicht sehen Sie jetzt keinen Grund dafür, aber wenn Sie sich auf ein statisches Final-Muster festlegen, können Sie den Classloader durch nichts anderes als einen Abriss re-initialisieren.

Im Fall von ObjectMapper ist das in Ordnung, aber im Allgemeinen ist es schlechte Praxis und es gibt keinen Vorteil gegenüber der Verwendung eines Singleton-Musters oder der Inversion-of-Control, um Ihre langlebigen Objekte zu verwalten.

35 Stimmen

Ich würde vorschlagen, dass, obwohl statische STATEFUL Singletons typischerweise ein Gefahrenzeichen sind, es genug Gründe gibt, warum in diesem Fall die gemeinsame Nutzung einer einzelnen (oder einer kleinen Anzahl von) Instanzen sinnvoll ist. Man kann dafür Dependency Injection verwenden; aber gleichzeitig sollte man sich fragen, ob es ein tatsächliches oder potentielles Problem zu lösen gibt. Dies gilt insbesondere für das Testen: Nur weil etwas in einem bestimmten Fall problematisch sein könnte, heißt das noch lange nicht, dass es das auch für Ihre Anwendung ist. Also: sich der Probleme bewusst sein, gut. Eine Einheitsgröße für alle anzunehmen, ist nicht so gut.

4 Stimmen

Natürlich ist es wichtig, die Probleme zu verstehen, die mit jeder Designentscheidung verbunden sind, und wenn man etwas tun kann, ohne Probleme für den Anwendungsfall zu verursachen, wird man per Definition keine Probleme verursachen. Ich würde jedoch argumentieren, dass die Verwendung statischer Instanzen keine Vorteile bringt und in der Zukunft erhebliche Probleme verursachen kann, wenn sich Ihr Code weiterentwickelt oder an andere Entwickler weitergegeben wird, die Ihre Designentscheidungen möglicherweise nicht verstehen. Wenn Ihr Framework Alternativen unterstützt, gibt es keinen Grund, statische Instanzen nicht zu vermeiden, denn sie haben sicherlich keine Vorteile.

11 Stimmen

Ich denke, diese Diskussion geht in sehr allgemeine und wenig nützliche Richtungen. Ich habe kein Problem damit, vorzuschlagen, dass es gut ist, gegenüber statischen Singletons misstrauisch zu sein. Ich bin nur zufällig sehr vertraut mit der Verwendung in diesem speziellen Fall, und ich glaube nicht, dass man aus einer Reihe allgemeiner Leitlinien spezifische Schlussfolgerungen ziehen kann. Ich werde es also dabei belassen.

1voto

Harshit Punkte 902

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)

Die Methode _hashMapSuperInterfaceChain in der Klasse com.fasterxml.jackson.databind.type.TypeFactory synchronisiert ist. Bei hohen Lasten kommt es zu Konflikten.

Dies könnte ein weiterer Grund sein, einen statischen ObjectMapper zu vermeiden

4 Stimmen

Überprüfen Sie die neuesten Versionen (und geben Sie vielleicht die Version an, die Sie hier verwenden). Es gab Verbesserungen beim Sperren aufgrund von gemeldeten Problemen, und die Typauflösung (z.B.) wurde für Jackson 2.7 komplett neu geschrieben. Obwohl in diesem Fall, TypeReference ist etwas teuer in der Anwendung: wenn möglich, sollte es in JavaType würde eine ganze Menge an Verarbeitung vermeiden ( TypeReference s können - leider - aus Gründen, die ich hier nicht näher erläutern möchte, nicht gecached werden), da sie "vollständig aufgelöst" sind (Supertyp, generische Typisierung usw.).

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X