Comment traiter Singleton avec la sérialisation

Considère que j’ai une classe Singleton définie comme suit.

public class MySingleton implements Serializable{ private static MySingleton myInstance; private MySingleton(){ } static{ myInstance =new MySingleton(); } public static MySingleton getInstance(){ return MySingleton.myInstance; } } 

La définition ci-dessus selon moi satisfait aux exigences d’un Singleton. Le seul comportement supplémentaire ajouté est que la classe implémente une interface sérialisable.

Si une autre classe X récupère l’instance du single et l’écrit dans un fichier et la désérialise ultérieurement pour obtenir une autre instance, nous aurons deux instances qui vont à l’encontre du principe de Singleton.

Comment puis-je éviter cela ou suis-je mal dans la définition ci-dessus elle-même.

La meilleure façon de faire est d’utiliser le modèle enum singleton:

 public enum MySingleton { INSTANCE; } 

Cela garantit la singularité de l’object et vous offre une possibilité de sérialisation de telle sorte que vous obteniez toujours la même instance.

Plus généralement, vous pouvez fournir une méthode readResolve() comme readResolve() :

 protected Object readResolve() { return myInstance; } 

@ColinD est un peu juste, mais sa réponse illustre aussi pourquoi les singletons n’emballent pas vraiment la sérialisation.

Voici ce qui se passe lorsque vous sérialisez une valeur enum (voir ici ).

Les règles pour la sérialisation d’une instance enum diffèrent de celles pour la sérialisation d’un object sérialisable “ordinaire”: la forme sérialisée d’une instance enum consiste uniquement en son nom de constante enum, avec des informations identifiant son type enum de base. Le comportement de désérialisation diffère également: les informations de classe sont utilisées pour trouver la classe enum appropriée et la méthode Enum.valueOf est appelée avec cette classe et le nom de la constante reçue afin d’obtenir la constante enum à renvoyer.

Ainsi, tout état supplémentaire que vous attachez à vos valeurs enum ne survit pas à la sérialisation et à la désérialisation.

Vous pouvez faire la même chose en ajoutant du code de sérialisation / désérialisation personnalisé à vos classes singleton. Ce code devrait soit ne pas enregistrer l’état du singleton, soit le jeter lorsque le singleton est désérialisé. De toute façon, vous readResolve() la logique dans une méthode readResolve() comme expliqué par la réponse de @ ColinD.

Maintenant, je suppose que la raison pour laquelle vous voulez sérialiser des singletons est que vous voulez conserver leur état. Malheureusement, cela pose un problème conceptuel. Supposons que votre application ait instancié le singleton dans le cours normal des événements, puis désérialise un graphe d’object comprenant une copie d’une instance précédente du singleton. Qu’est-ce que ça peut faire?

  • S’il désérialise le singleton normalement, il viole “singleton-ness”.
  • Si ce n’est pas le cas, l’application ne peut pas accéder à l’état précédent du singleton.

La solution avec enum ne fonctionnera pas avec Singletons géré par Spring, EJB, Guice ou tout autre framework DI. Cela ne fonctionne qu’avec les énumérations, car l’énumération est traitée spécialement par l’algorithme de sérialisation.

Tout d’abord, les singletons n’ont pas besoin de sérialisation , car si vous les désérialisez, puis désérialisez singleton! = YourSingleton.getInstance (), cela signifierait que vous avez deux instances de votre singleton, ce qui signifie que YourSingleton n’est pas du tout un singleton, ce qui peut conduire à des bogues imprévisibles.

Cependant, vous devez parfois sérialiser non-singleton qui contient une référence à singleton. La solution est simple:

 class NonSingleton implements Serializable { private transient YourSingleton singleton = YourSingleton.getInstance(); ... } 

Avec le spring:

 @Configurable class NonSingleton implements Serializable { @Autowired private transient YourSingleton singleton; ... } 

Voici ci-dessous ma classe Singleton qui implémente l’interface Serializable . Marquez qu’il contient la méthode readResolve() également.

 import java.io.Serializable; public class Singleton implements Serializable { private static Singleton singleton = new Singleton( ); public int i = 1; private Singleton() { } public static Singleton getInstance( ) { return singleton; } public Object readResolve() { return getInstance( ); } public static void main(Ssortingng[] args) { Singleton s1 = getInstance(); System.out.println(s1.hashCode()); Singleton s2 = getInstance(); System.out.println(s2.hashCode()); } } 

Vous trouverez ci-dessous la classe qui va d’abord sérialiser puis désérialiser la classe ci-dessus. Ici, la désérialisation a lieu deux fois, mais une seule fois, une seule fois sera créée à cause de la méthode readResolve ().

 public class SingletonSerializableDemo { static Singleton sing = Singleton.getInstance(); static Singleton s1 = null; static Singleton s2 = null; public static void main(Ssortingng[] args) { try { FileOutputStream fileOut = new FileOutputStream("E:/singleton.ser"); ObjectOutputStream out = new ObjectOutputStream(fileOut); out.writeObject(sing); out.close(); fileOut.close(); System.out.println("Serialized data is saved"); FileInputStream fileIn1 = new FileInputStream("E:/singleton.ser"); FileInputStream fileIn2 = new FileInputStream("E:/singleton.ser"); ObjectInputStream in1 = new ObjectInputStream(fileIn1); ObjectInputStream in2 = new ObjectInputStream(fileIn2); s1 = (Singleton) in1.readObject(); s2 = (Singleton) in2.readObject(); System.out.println(s1.hashCode() + " "+ s1.i); s1.i = 10; System.out.println(s2.hashCode() + " "+ s2.i); in1.close(); in2.close(); fileIn1.close(); fileIn2.close(); }catch(Exception i) { i.printStackTrace(); } } } 

Et le résultat sera:

Les données sérialisées sont enregistrées
21061094 1
21061094 10

Conclusion: La classe Singleton peut également être sérialisée en conservant la méthode readResolve() dans la classe Singleton.

Disons que nous avons la classe singleton suivante:

 public class ConnectionFactory implements Serializable { private static ConnectionFactory INSTANCE; private ConnectionFactory() { } public static ConnectionFactory getInstance() { if (INSTANCE == null) { synchronized(ConnectionFactory.class) { if(INSTANCE == null) INSTANCE = new ConnectionFactory(); } } return INSTANCE; } } 

Maintenant, nous avons la classe principale comme ci-dessous pour la sérialisation et la désérialisation des objects:

  public static void main(Ssortingng[] args) throws FileNotFoundException, IOException, ClassNotFoundException { ConnectionFactory INSTANCE=ConnectionFactory.getInstance(); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("connFactory.ser")); oos.writeObject(INSTANCE); oos.close(); // Here I am recreating the instance by reading the serialized object data store ObjectInputStream ois = new ObjectInputStream(new FileInputStream("connFactory.ser")); ConnectionFactory factory1 = (ConnectionFactory) ois.readObject(); ois.close(); // I am recreating the instance AGAIN by reading the serialized object data store ObjectInputStream ois2 = new ObjectInputStream(new FileInputStream("connFactory.ser")); ConnectionFactory factory2 = (ConnectionFactory) ois2.readObject(); ois2.close(); // Let's see how we have broken the singleton behavior System.out.println("Instance reference check->" +factory1.getInstance()); System.out.println("Instance reference check->" +factory2.getInstance()); System.out.println("========================================================="); System.out.println("Object reference check->" + factory1); System.out.println("Object reference check->" + factory2); } 

Donc, si nous exécutons le code ci-dessus, nous obtiendrons le comportement suivant: “il a créé deux objects et une référence statique pour INSTANCE. Cela signifie que si nous lisons plusieurs fois le format sérialisé d’un object singleton, nous créerons plusieurs objects. un object singleton est censé faire. Alors pouvons-nous éviter i ?, Oui, nous le pouvons. “

Pour éviter plusieurs instances de la classe singleton, nous utiliserons la méthode suivante fournie par la sérialisation:

 private Object readResolve() throws ObjectStreamException { return INSTANCE; } 

Cela empêchera la création de plusieurs instances d’une classe singleton.

Je pense que Singletons peut être sérialisé et voici le code sur la façon de le faire:

 import java.io.Serializable; public class MySingleton implements Serializable { private MySingleton(Ssortingng name) { this.name = name; } private static MySingleton mySingleton; private Ssortingng name; public Ssortingng getName() { return name; } public void setName(Ssortingng name) { this.name = name; } public static MySingleton getInstance(Ssortingng name) { if(mySingleton == null) { System.out.println("in if..."); mySingleton = new MySingleton(name); } return mySingleton; } } 

et voici la méthode “main” qui obtient l’instance de la classe Singleton ci-dessus, la sérialise et la désérialise:

 public static void main (Ssortingng[] args) { MySingleton m = MySingleton.getInstance("Akshay"); try { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D://temp.ser")); oos.writeObject(m); ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D://temp.ser")); MySingleton m2 = (MySingleton) ois.readObject(); System.out.println(m2.getName()); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } 

et la sortie est: –

dans si …

Akshay

Merci.

Cela peut être une solution familière mais juste au cas où pour référence.

 public class ConnectionFactory implements Serializable { //Static variable for holding singleton reference object private static ConnectionFactory INSTANCE; /** * Private constructor */ private ConnectionFactory() { } /** * Static method for fetching the instance * * @return */ public static ConnectionFactory getIntance() { //Check whether instance is null or not if (INSTANCE == null) { //Locking the class object synchronized (ConnectionFactory.class) { //Doing double check for the instance //This is required in case first time two threads simultaneously invoke //getInstance().So when another thread get the lock,it should not create the //object again as its already created by the previous thread. if (INSTANCE == null) { INSTANCE = new ConnectionFactory(); } } } return INSTANCE; } /** * Special hook provided by serialization where developer can control what object needs to sent. * However this method is invoked on the new object instance created by de serialization process. * * @return * @throws ObjectStreamException */ private Object readResolve() throws ObjectStreamException { return INSTANCE; } } 

Test du code

 public class SerializationTest { public static void main(Ssortingng[] args) { ConnectionFactory INSTANCE = ConnectionFactory.getIntance(); try { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("connectFactory.ser")); oos.writeObject(INSTANCE); oos.close(); ObjectInputStream osi = new ObjectInputStream(new FileInputStream("connectFactory.ser")); ConnectionFactory factory1 = (ConnectionFactory) osi.readObject(); osi.close(); ObjectInputStream osi2 = new ObjectInputStream(new FileInputStream("connectFactory.ser")); ConnectionFactory factory2 = (ConnectionFactory) osi2.readObject(); osi2.close(); System.out.println("Instance reference check->" + factory1.getIntance()); System.out.println("Instance reference check->" + factory2.getIntance()); System.out.println("==================================================="); System.out.println("Object reference check->" + factory1); System.out.println("Object reference check->" + factory2); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } 

Sortie

 Instance reference check->com.javabrains.ConnectionFactory@6f94fa3e Instance reference check->com.javabrains.ConnectionFactory@6f94fa3e =================================================== Object reference check->com.javabrains.ConnectionFactory@6f94fa3e Object reference check->com.javabrains.ConnectionFactory@6f94fa3e 

Les classes Singleton ressemblent à un gestionnaire ou à un contrôleur et, en général, nous ne voulons pas sauvegarder l’état d’un contrôleur au lieu de l’entité. En général, nous devons enregistrer l’état de l’object de toute entité et non du contrôleur.
Singleton est singleton pour un chargeur de classe unique pas pour le chargeur de classes multiples. Si une classe est chargée dans un chargeur de classes, l’autre chargeur de classes ne le saura pas et se comportera ainsi.