2011-03-10 14 views
5

Bir ObjectOutputStreamtransient anahtar sözcüğünü kullanmadan ve serialPersistentFields-dizini tanımlamaksızın serileştirilebilir bir sınıfın hangi alanlara serileştirilmesi gerektiğini anlatan bir yol var mı?Geçici veya serialPersistentFields kullanmadan ObjectOutputStream'de hangi alanların (değil) serileştirildiğini belirtin


Arka plan: Bir sınıfın hangi üyelerine serileştirilmesi gerektiğini tanımlamak için ek açıklamaları kullanmalıyım (veya daha iyisi: serileştirilmemelidir). İlgili sınıflar Serializable arabirimini uygulamalıdır, ancak Externalizable DEĞİL, bu nedenle her nesne için serileştirme/serileştirme algoritmasını uygulamak istemiyorum, bunun yerine yalnızca açıklamaları kullanın. Xzx157 anahtar sözcüğünü kullanamıyorum çünkü ek açıklama, bir alanın serileştirilip geçirilmeyeceğini belirlemek için bazı ek kontroller gerektiriyor. Bu kontroller ObjectOutputStream (veya kendi ObjectOutputStream alt sınıfımda) tarafından yapılmalıdır. Ayrıca, her sınıfta bir serialPersistentFields -array tanımlayamıyorum çünkü daha önce açıklandığı gibi, derleme zamanında hangi alanların serileştirilmesi gerektiği tanımlanmamıştır.

Etkilenen sınıfta not edilmesi gereken tek şey alan düzeyinde (@Target(ElementType.FIELD)) ek açıklamadır.

Son birkaç gün içinde epeyce yaklaşımı denedim, fakat çalışmayı bulamadım:


ObjectOutputStream, kendi tanımlamak için kullanılabilecek bir writeObjectOverride(Object) yöntemine sahiptir. ObjectOutputStream uzatılırken serileştirme işleminin uygulanması. Bu yalnızca ObjectOutputStream, argüman-yapıcı ile başlatılmışsa çalışır, çünkü aksi halde writeObjectOverride asla çağrılmaz. Ancak bu yaklaşım benim serileştirme sürecini tek başıma uygulamamı gerektiriyor ve bunu yapmak istemiyorum, çünkü oldukça karmaşık ve zaten ObjectOutputStream tarafından zaten uygulanıyor. Varsayılan serileştirme uygulamasını değiştirmenin bir yolunu arıyorum.


Başka bir yaklaşım ObjectOutputStream'i tekrar genişletiyor ve writeObjectOverride(Object)'u (enableReplaceObject(true) çağırdıktan sonra) geçersiz kılıyordu. Bu yöntemde, seri hale getirilmiş nesneyi, serileştirilmesi gereken bir Alanlar Listesi'ni tanımlayan bir proxy'de kapsüllemek için bir çeşit SerializationProxy (bkz. Xzx147) kullanmayı denedim. Ancak bu yaklaşım, writeObjectOverride olarak da başarısız olur ve sonra da Proxy'deki sonsuz bir döngü ile sonuçlanan Alanların Listesi (List<SerializedField> fields) için çağrılır.


Çalışma zamanında değiştiricilerin değiştirilebileceğini öğrendiğimde (bkz. Change private static final field using Java reflection) Karşılık gelen ek açıklama ayarlanmışsa çalışma zamanında geçici-Değiştiriciyi ayarlamaya çalıştım . Ne yazık ki bu da işe yaramaz, çünkü önceki linkte kullanılan yaklaşım sadece statik alanlarda çalışmaktadır. Statik olmayan alanlarla çalışırken, bir istisna olmadan çalışır, ancak kalıcı değildir çünkü Field.class.getDeclaredField(...), etkilenen alanların her defasında yeni etki alanlarını döndürür:

public void setTransientTest() throws SecurityException, 
      NoSuchFieldException, IllegalArgumentException, 
      IllegalAccessException { 
     Class<MyClass> clazz = MyClass.class; 
     // anyField is defined as "private String anyField" 
     Field field = clazz.getDeclaredField("anyField"); 

     System.out.println("1. is " 
       + (Modifier.isTransient(field.getModifiers()) ? "" : "NOT ") 
       + "transient"); 

     Field modifiersField = Field.class.getDeclaredField("modifiers"); 
     boolean wasAccessible = modifiersField.isAccessible(); 
     modifiersField.setAccessible(true); 
     modifiersField.setInt(field, field.getModifiers() | Modifier.TRANSIENT); 
     modifiersField.setAccessible(wasAccessible); 

     System.out.println("2. is " 
       + (Modifier.isTransient(field.getModifiers()) ? "" : "NOT ") 
       + "transient"); 

     Field field2 = clazz.getDeclaredField("anyField"); 

     System.out.println("3. is " 
       + (Modifier.isTransient(field2.getModifiers()) ? "" : "NOT ") 
       + "transient");  
} 

Çıktı:

1. is NOT transient 
2. is transient 
3. is NOT transient 

Tekrar getDeclaredField çağrıldıktan sonra (Field field2 = clazz.getDeclaredField("anyField");) geçici değiştiriciyi zaten kaybetti.


Sonraki yaklaşım:
ObjectOutputStream'ü uzatın ve ObjectOutputStream.PutField putFields()'i geçersiz kılın ve kendi PutField uygulamasını gerçekleştirin. PutField hangi (ek) alanların serileştirildiğini belirtmenize izin verir, ancak ne yazık ki arayüzde put(String name, <type> val) formunda çok fazla metod vardır ve bunları uygularken metod çağrılarını çağrıldığı sınıf alanı ile ilişkilendiremiyorum. Örneğin private String test = "foo" olarak bildirilen bir alanı serileştirirken put("test", "foo") yöntemi çağrılır, ancak name değerini (test olan) test alanını içeren sınıfla ilişkilendiremiyorum çünkü içerik sınıfına herhangi bir gönderme yapılmaz ve test alanı için belirtilen ek açıklamayı okuyun.


Ayrıca birkaç diğer yaklaşımı denedim ama daha önce de bahsettiğim gibi DontSerialize ek açıklamaları dışında kalan tüm alanları başarılı bir şekilde serileştiremedim.

Karşılaştığım bir şey de ByteCode manipülatörleri idi. Belki bunlarla mümkündür ama herhangi bir harici araç kullanmama şartım var - saf Java (1.5 veya 1.6) olması gerekiyor.


Bu gerçekten uzun mesaj için üzgünüz, ama daha önce denediğimi göstermek istedim ve birisinin bana yardım edebileceğini umuyorum. Şimdiden teşekkürler.

+1

Bu biraz ağır bir okuma .... –

+0

Bana neden geçici anahtar sözcüğünü kullanamazsınız açık değil. Görünüşe göre, bir sebepten dolayı bir Ek Açıklama kullanmanız gerekiyor, ancak bu sizi geçici olarak kullanmaz. –

+1

Belki de 'DontSerialize' kullanmak kötü bir örnekti. Açıklama eklediğimin "DontSerializeOnMondays" (bu açıklamanın amacını sorgulama, sadece bir örnek) olduğunu söyleyelim. ObjectOutputStream'in bir nesneyi serileştirmesi gerektiğinde, o zaman haftanın gününün bir pazartesi olup olmadığını kontrol eder ve eğer öyleyse, ek açıklama alanı ile ilişkilendirilir ancak sınıftaki diğer tüm alanlar serileştirilmez. Dolayısıyla, alanı içeren sınıfın kodunu yazarken, serileştirme işleminin bir pazartesi günü gerçekleştirilip gerçekleştirilmeyeceğini bilmiyorum - bu yüzden "geçici" anahtar kelimesini tanımlamam gerekmiyor mu bilmiyorum. – m0m0

cevap

0

"Serileştirme" gerçekten yapmak istediğiniz şey ise yeniden düşünürdüm. Seri hale getirme kurallarının çalışma zamanında tanımlanan bazı mantıklara bağlı olduğu düşünüldüğünde, Deserialization işlemi yazılacak bir kabus olacaktır.

İlginç sorun olsa da.

İlgili konular