Jackson'ın ObjectMapper'ını statik bir alan olarak ilan etmeli miyim?


362

Jackson kütüphanesinin ObjectMappersınıfı iş parçacığı için güvenli görünüyor .

Bu, benim ObjectMapperböyle statik bir alan olarak ilan etmem gerektiği anlamına mı geliyor?

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

bunun yerine örnek düzeyinde bir alan olarak mı?

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

Yanıtlar:


506

Evet, güvenli ve tavsiye edilir.

Yönlendirdiğiniz sayfadaki tek uyarı, eşleştiriciyi paylaştıktan sonra yapılandırmasını değiştiremeyeceğinizdir; ancak yapılandırmayı değiştirmiyorsunuz, böylelikle sorun yok. Yapılandırmayı değiştirmeniz gerekiyorsa, bunu statik bloktan yaparsınız ve bu da iyi olur.

DÜZENLEME : (2013/10)

2.0 ve üstü ile, daha da iyi bir yol olduğunu belirterek yukarıda: artırılabilir: kullanım ObjectWriterve ObjectReadernesneler tarafından inşa edilebilir ObjectMapper. Bunlar tamamen değiştirilemez, iş parçacığı açısından güvenlidir, yani iş parçacığı güvenliği sorunlarına ( ObjectMapperkodun örneği yeniden yapılandırmaya çalıştığında ortaya çıkabilir) teorik olarak bile mümkün olmadığı anlamına gelir .


23
@StaxMan: Eğer ObjectMapperhala iş parçacığı sonra ObjectMapper#setDateFormat()çağrılırsa biraz endişe ediyorum . SimpleDateFormatİplik güvenli olmadığı bilinmektedir , bu nedenle ObjectMapperörneğin SerializationConfigher birinden önce klonlanmadıkça olmayacaktır writeValue()(şüpheliyim). Korkumu giderebilir misin?
dma_k

49
DateFormatgerçekten de kaputun altında klonlanır. Orada iyi şüphe var ama sen örtüsün. :)
StaxMan

3
Büyük bir kurumsal uygulamanın birim / entegrasyon testleri sırasında garip davranışlarla karşılaştım. ObjectMapper'ı statik son sınıf özniteliği olarak koyarken PermGen sorunları ile karşılaşmaya başladım. Herkes olası nedenleri açıklamak ister mi? Ben jackson-databind 2.4.1 sürümünü kullanıyordum.
Alejo Ceballos

2
@MiklosKrivan hiç baktın ObjectMappermı ?! Yöntemler adlandırılır writer()ve reader()(bazı ve readerFor(), writerFor()).
StaxMan

2
Hiçbir mapper.with()çağrı yok (Jackson'da "ile" yeni bir örnek oluşturma ve iş parçacığı için güvenli yürütme anlamına gelir). Ancak yapılandırma değişiklikleriyle ilgili: hiçbir kontrol yapılmadığından, yapılandırmaya erişim ObjectMapperkorunmalıdır. "Copy ()" ile ilgili olarak: evet, aynı kurallara göre tamamen (yeniden) yapılandırılabilen yeni bir kopya oluşturur: önce tamamen yapılandırın, sonra kullanın ve sorun değil. İlişkili önemsiz bir maliyet var (kopya önbelleğe alınan işleyicilerin hiçbirini kullanamayacağı için), ancak güvenli bir yol, evet.
StaxMan

53

ObjectMapper iş parçacığı güvenli olmasına rağmen, özellikle çok iş parçacıklı uygulamada, statik bir değişken olarak bildirmekten kesinlikle vazgeçerim. Kötü bir uygulama olduğu için değil, ağır bir kilitlenme riski taşıdığınız için bile. Kendi deneyimlerimden söylüyorum. Web hizmetlerinden JSON verilerini alıp işleyen 4 özdeş iş parçacığına sahip bir uygulama oluşturdum. Benim iş parçacığı döküm göre, aşağıdaki komut sık sık durdu:

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

Bunun yanı sıra, performans iyi değildi. Statik değişkeni örnek tabanlı değişkenle değiştirdiğimde, durma kayboldu ve performans dört kat arttı. 2.4 milyon JSON dokümanı, 2.5 saat yerine 40 dakika 556 saniyede işlendi.


16
Gary'nin cevabı tamamen mantıklı. Ancak ObjectMapper, her sınıf örneği için bir örnek oluşturmaya devam etmek kilitleri engelleyebilir, ancak daha sonra GC'de çok ağır olabilir (oluşturduğunuz sınıfın her örneği için bir ObjectMapper örneği hayal edin). Orta yol yaklaşımı, uygulamada yalnızca bir (genel) statik ObjectMapperörneği tutmak yerine, her sınıfta (özel) statik bir örneği bildirebilirsiniz . Bu, global bir kilidi azaltacaktır (yükü sınıf olarak dağıtarak) ve yeni bir nesne oluşturmayacak, dolayısıyla GC'ye de ışık verecektir. ObjectMapper
Abhidemon

Ve elbette, bir ObjectPool şeyi korumak, devam edebileceğiniz en iyi yoldur - böylece en iyi GCve Lockperformansları verir. Apache-common ObjectPooluygulaması için aşağıdaki bağlantıya başvurabilirsiniz . commons.apache.org/proper/commons-pool/api-1.6/org/apache/…
Abhidemon

17
Ben bir alternatif öneriyorum: bir ObjectMapperyerde statik tutmak , ama sadece ObjectReader/ ObjectWriterörnekleri (yardımcı yöntemler aracılığıyla) almak, başka yerlerde (veya dinamik çağrı) referansları tutmak. Bu okuyucu / yazıcı nesneleri yalnızca tam olarak iş parçacığı açısından güvenli wrt yeniden yapılandırması değil, aynı zamanda çok hafiftir (wrt eşleyici örnekleri). Bu nedenle binlerce referansın tutulması fazla bellek kullanımı sağlamaz.
StaxMan

Yani yani ipler jackson 2.8.x kullanarak, başka bir iş parçacığı üzerinde bekleyen bloke alışkanlık, objectReader.readTree parçacıklı uygulamada denir demek engellemediğini ObjectReader örneklerine çağrı vardır
Xephonia

1

İş parçacığı güvenliği açısından statik bir ObjectMapper bildirmek güvenli olsa da, Java'da statik Nesne değişkenleri oluşturmanın kötü uygulama olarak kabul edildiğinin farkında olmalısınız. Daha fazla ayrıntı için bkz. Statik değişkenler neden kötü olarak değerlendiriliyor? (ve isterseniz cevabım )

Kısacası, özlü birim testleri yazmayı zorlaştırdığı için statikten kaçınılmalıdır. Örneğin, statik bir son ObjectMapper ile, sahte kod için JSON serileştirmeyi değiştiremezsiniz veya işlem yapmazsınız.

Ayrıca, statik bir final çalışma zamanında ObjectMapper'ı yeniden yapılandırmanızı önler. Bunun için şimdi bir neden düşünmeyebilirsiniz, ancak kendinizi statik bir son desene kilitlerseniz, sınıf yükleyiciyi yırtmaktan başka hiçbir şey onu yeniden başlatmanıza izin vermez.

ObjectMapper söz konusu olduğunda, ancak genel olarak kötü bir uygulamadır ve uzun ömürlü nesnelerinizi yönetmek için tektonlu bir desen veya kontrolün ters çevrilmesinin avantajı yoktur.


27
Statik STATEFUL singletonların tipik olarak bir tehlike işareti olmasına rağmen, bu durumda tek (veya az sayıda) örneği paylaşmanın mantıklı olmasının yeterli nedenleri olduğunu öneririm. Bunun için Bağımlılık Enjeksiyonu kullanmak isteyebilirsiniz; ancak aynı zamanda çözülmesi gereken gerçek veya potansiyel bir sorun olup olmadığını sormaya değer. Bu özellikle testler için geçerlidir: bir şeyin bir durumda sorunlu olabilmesi, kullanımınız için olduğu anlamına gelmez. Yani: sorunların farkında olmak harika. "Bir beden herkese uyar" varsayarsak, o kadar iyi değil.
StaxMan

3
Açıkçası, herhangi bir tasarım kararı ile ilgili sorunları anlamak önemlidir ve kullanım durumunuz için sorun yaratmadan bir şey yapabiliyorsanız, tanım gereği herhangi bir soruna neden olmazsınız. Bununla birlikte, statik örneklerin kullanımının hiçbir faydası olmadığını ve kodunuz geliştikçe veya tasarım kararlarınızı anlayamayabilecek diğer geliştiricilere verildiğinde gelecekte önemli sorunlara kapı açtığını iddia ediyorum. Çerçeveniz alternatifleri destekliyorsa, statik örneklerden kaçınmak için hiçbir neden yoktur, kesinlikle bunlardan hiçbir avantajı yoktur.
JBCP

11
Bu tartışmanın çok genel ve daha az yararlı teğetlere girdiğini düşünüyorum. Ben statik singleton şüpheli iyi olduğunu gösteren bir sorunum yok. Bu özel durum için çok tanıdık geliyor ve genel yönerge setinden belirli sonuçlara ulaşabileceğini düşünmüyorum. Ben de bırakacağım.
StaxMan

1
Geç yorum, ancak özellikle ObjectMapper bu görüşe katılmıyor mu? Bu ortaya çıkarır readerForve writerForhangi oluşturmak ObjectReaderve ObjectWritertalep üzerine örnekler. Yani ilk yapılandırma ile haritacı statik bir yere koymak söyleyebilirim, sonra ihtiyacınız olan her durumda yapılandırma ile okuyucular / yazarlar olsun?
Carighan

1

Statik bir son değişken olarak tanımlamak istemiyorsanız, ancak biraz ek yükü kaydetmek ve ipliğin güvenli olmasını garanti etmek istiyorsanız , bu PR'dan öğrendiğim bir numara .

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

yazarın kredisi.


2
Ancak ObjectMapper, bir havuzun parçası olabilecek iş parçacığına bağlı olacağından bellek sızıntısı riski vardır .
Kenston Choi

@KenstonChoi Sorun olmamalı AFAIU. İş parçacıkları gelir ve gider, yerel iş parçacıkları gelir ve iş parçacıklarıyla birlikte gelir. Eşzamanlı iş parçacığı miktarına bağlı olarak, bellek göze veya göze alamaz, ancak "sızıntı" görmüyorum.
Ivan Balashov

2
@IvanBalashov, ancak iş parçacığı bir iş parçacığı havuzuna (örneğin, Tomcat gibi kaplar) oluşturulduysa / geri döndüğünde, kalır. Bu bazı durumlarda istenebilir, ancak bilmemiz gereken bir şey.
Kenston Choi

-1

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)

Com.fasterxml.jackson.databind.type.TypeFactory sınıfındaki _hashMapSuperInterfaceChain yöntemi senkronize edilir. Yüksek yüklerde aynı çekişmeyi görüyorum.

Statik bir ObjectMapper'ı önlemek için başka bir neden olabilir


1
En son sürümleri kontrol ettiğinizden emin olun (ve belki de burada kullandığınız sürümü belirtin). Bildirilen sorunlara dayalı olarak kilitleme konusunda iyileştirmeler yapıldı ve Jackson 2.7 için tür çözünürlüğü (f.ex) tamamen yeniden yazıldı. Bu durumda, TypeReferencekullanımı biraz pahalı olsa da: mümkünse, JavaTypebiraz işlemden kaçınmak için çözmek ( TypeReferencene yazık ki - buraya girmeyeceğim nedenlerden dolayı önbelleğe alınamaz), çünkü "tam çözümlenmiş" (süper tip, genel yazım vb.).
StaxMan
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.