2011-10-27 7 views
23

Tengo un singleton como este.java Singleton: evitar la creación múltiple a través de la reflexión

public class BookingFactory { 

    private final static BookingFactory instance; 

    static { 
     instance = new BookingFactory(); 
    } 

    public static BookingFactory getInstance() { 
     return instance; 
    } 

    private BookingFactory() { 
     System.out.println("Object is created."); 
    } 
} 

public class Test { 
    BookingFactory instance = BookingFactory.getInstance(); 
    instance = BookingFactory.getInstance(); 

    Class<?> clazz = Class.forName("com.test.BookingFactory"); 

    Constructor pvtConstructor = clazz.getDeclaredConstructors()[0]; 

    // Set its access control 
    pvtConstructor.setAccessible(true); 

    // Invoke Private Constructor 
    BookingFactory notSingleton = (BookingFactory) pvtConstructor.newInstance(null); 
} 

Cuando ejecuto esto, vi más de un mensaje de impresión. ¿Hay alguna manera de evitar que este singleton sea instanciado más de una vez desde esta reflexión?

Gracias.

+3

La verdadera pregunta es por qué sería tan interesados ​​en impedir que? Si un programador llega a esa cantidad de problemas, entonces significaría que intentará realizar ingeniería inversa del singleton de alguna manera para adaptarse a un caso especial que no fue considerado durante el diseño. No digo que sería la idea más brillante, pero al mismo tiempo, todo esto es hipotético de todos modos. –

+1

1 muy bien presentada cuestión, especialmente el isómero [SSCCE] (http://pscode.org/sscce.html) – Bohemian

Respuesta

13

Hacer la afirmación en el constructor:

private BookingFactory() { 
    if (instance != null) 
     throw new IllegalStateException("Only one instance may be created"); 
    System.out.println("Object is created."); 
} 
+0

¿Sería ésta una multi-hilo? – Kaunteya

+1

@Kaunteya sí, cuando se usa con código en la pregunta, es threadsafe, porque 'instance' se crea en un bloque estático que se ejecuta en el tiempo de carga de la clase (no se inicializa perezoso) y el cargador de clases tiene su propia seguridad integrada. La JVM garantiza que toda la inicialización estática se completará antes de que se pueda usar la clase, lo que incluye los constructores de llamadas. – Bohemian

16

Trate de usar un enum. las enumeraciones son buenas Singletons.

public static enum BookingFactory { 
    INSTANCE; 
    public static BookingFactory getInstance() { 
     return INSTANCE; 
    } 
} 

No puede crear una enumeración a través de la reflexión.

El método getInstance() es superfluo, pero hace que sea más fácil para ejecutar la prueba, lanzando la siguiente excepción:

java.lang.IllegalArgumentException: Cannot reflectively create enum objects 
    at java.lang.reflect.Constructor.newInstance(Constructor.java:530) 
    at MultiSingletonTest.main(MultiSingletonTest.java:40) 

Oh, mira, alguien ya dio la respuesta enumeración. Publicación de todos modos para una mayor integridad.

+0

Iba yendo y viniendo solo para votar como un duplicado. Probablemente debería ser. –

+0

probablemente deberían cerrarse automáticamente como duplicados a cualquier pregunta de Java que contenga la palabra singleton pero no enum;) – laher

+0

enum no puede heredar. A veces queremos que Singleton herede de otra clase, por ejemplo java.util.Observable. – simpatico

1

Si su producto único en realidad no almacenar el estado, entonces su mejor opción es no utilizar un producto único. En su lugar, implemente la fábrica como un método estático libre de estados.

8

Adaptado de Making the Java Singleton Reflection Proof when using Lazy Loading:

package server; 

import java.lang.reflect.ReflectPermission; 
import java.security.*; 


public class JavaSingleton { 

    private static JavaSingleton INSTANCE = null; 

    private JavaSingleton() { 
    ReflectPermission perm = new ReflectPermission("suppressAccessChecks", ""); 
    AccessController.checkPermission(perm); 
    } 


    synchronized public static final JavaSingleton getInstance() { 
    if (INSTANCE == null) { 
     AccessController.doPrivileged(new PrivilegedAction<Object>() { 
     public Object run() { 
      INSTANCE= new JavaSingleton(); 
      return null; 
     } 
     }); 
    } 
    return INSTANCE; 
    } 

El constructor tiene una comprobación para ver si la persona que llama tiene acceso a ella. Como explica el enlace, será necesario crear un archivo de política que permita que la clase Singleton llame al constructor.

método de lanzar una excepción no impide que un cliente de llamar reflexivamente el constructor antes de getInstance() se llama de Bohemia. Aunque asegura que solo se crea una instancia, no hay garantía de que esto se haga con el método de la clase Singleton 'getInstance(). comprobación de control de

El acceso evitará esta ejemplificación no deseado.

1
import java.io.Serializable; 

public class Singleton implements Serializable,Cloneable{ 

private static final long serialVersionUID = 1L; 
private static Singleton singleton=null; 
//private static volatile Singleton singleton=null; 
private Singleton() { 
    if(singleton!=null){ 
     throw new RuntimeException("Its Singleton Class use getInstance method for object creation"); 
    } 
} 

public static Singleton getInstance(){ 
    return Holder.singleton; 

} 

/**** 
* good way for getting the instance. No need to worry about 
* BillPughSingleton 
*/ 
private static class Holder{ 
    private static final Singleton singleton=new Singleton(); 
} 

/*** 
/* 
* Use this code for preventing Singleton breakage in multi threading scenario and comment above getInstance method 
* As this is the efficient way 
* If we put synchronized at method level level then will impact performance and will executed every time when getInstance is called 
* But if once the instance is created then there is no need for synchronized. 
*/ 

/* public static Singleton getInstance(){ 
    if(singleton==null){ 
     synchronized (Singleton.class) { 
      if(singleton==null){ 
       singleton=new Singleton(); 
      } 
     } 

    } 
    return singleton; 

}*/ 

@Override 
public Object clone() throws CloneNotSupportedException{ 
    /*** 
    * We can place below check OR we can remove the exception thrown check and return singleton instead of super.clone() 
    * Use any one way 
    */ 
    if(singleton!=null){ 
     throw new RuntimeException("Its Singleton Class use getInstance method for object creation"); 
    } 
    return super.clone(); 
} 
/*** 
* 
* To Prevent breaking of singleton pattern by using serilization/de serilization 
*/ 
private Object readResolve(){ 
    System.out.println("Read Resolve executed"); 
    return singleton; 
} 
} 

** ** Prueba Singleton

import java.io.FileInputStream; 
import java.io.FileNotFoundException; 
import java.io.FileOutputStream; 
import java.io.IOException; 
import java.io.ObjectInputStream; 
import java.io.ObjectOutputStream; 
import java.lang.reflect.Constructor; 
import java.lang.reflect.InvocationTargetException; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 

/*** 
* 
* Ways to prevent break Singleton 
*/ 
public class Main { 

private static ObjectInputStream inputStream; 

public static void main(String[] args) throws Exception { 
    Singleton orginalSingletonObject = Singleton.getInstance(); 

    /*** 
    * Singleton is broken by using Reflection 
    * We can prevent that by putting a check in private constructor of Singleton.java 
    * 
    */ 
    breakSingletonByReflection(orginalSingletonObject); 

    /*** 
    * By Serialization/De-Serialization break Singleton We need 
    * Serialization interface in a class needs to be serialized like 
    * Singleton.java 
    * 
    * To prevent breaking of singleton we can add readResolve method in Singleton.java 
    * readResolve is the method which returns the instance of the class when a serialized class is de serialized. 
    * So implement the readResolve method to return the same object. 
    * Hence prevent breaking of Singleton design pattern. 
    * Refer this link for more information on readResolve 
    * https://docs.oracle.com/javase/6/docs/platform/serialization/spec/input.html#5903 
    */ 
    breakSingletonByserialization(orginalSingletonObject); 

    /*** 
    * By Cloning break Singleton 
    * We need to implement Cloneable interface 
    * We can prevent that by putting a check in clone method of Singleton.java 
    */ 
    breakSingletonByCloning(orginalSingletonObject); 


    /*** 
    * Break Singleton By thread 
    * This scenario is related to multi-threading environment 
    * We can do this by putting double lock mechanism in Singleton.java and its good practice to use Volatile 
    * We can also prevent this scenario of breaking by creating object eagerly but its not good to create object eagerly 
    */ 

    breakSingletonByThreading(orginalSingletonObject); 
} 

private static void breakSingletonByThreading(Singleton orginalSingletonObject) { 

    ExecutorService executorService=Executors.newFixedThreadPool(2); 
    /** 
    * Run this code snippet after commenting the other code for better understanding 
    * Run it repeatly to create a condition when 2 threads enter the method getInstance() of Singleton class at a same time 
    * When 2 threads enter the getInstance method at same time they will get the singleton object as null (private static Singleton singleton in Singleton.java) 
    * Then they will create two different objects (have different hashcode) in this case singleton pattern will break. 
    */ 
    executorService.submit(Main::useSingleton); // JAVA 8 syntax it will get the singleton instance 
    executorService.submit(Main::useSingleton); 
    executorService.shutdown(); 
} 

public static void useSingleton(){ 
    Singleton singleton=Singleton.getInstance(); 
    printSingletonData("By Threading", singleton); 

} 




private static void breakSingletonByCloning(Singleton orginalSingletonObject) throws CloneNotSupportedException { 
    Singleton clonedSingletonObject=(Singleton) orginalSingletonObject.clone(); 
    printSingletonData("By Cloning", orginalSingletonObject, clonedSingletonObject); 
} 

private static void breakSingletonByReflection(Singleton orginalsingleton) 
     throws ClassNotFoundException, NoSuchMethodException, 
     InstantiationException, IllegalAccessException, 
     InvocationTargetException { 

    Class<?> singletonClass = Class.forName("SingletonTest.Singleton"); 
    @SuppressWarnings("unchecked") 
    Constructor<Singleton> constructor = (Constructor<Singleton>) singletonClass 
      .getDeclaredConstructor(); 
    constructor.setAccessible(true); 
    Singleton s = constructor.newInstance(); 
    printSingletonData("By Reflection", orginalsingleton, s); 
} 

private static void breakSingletonByserialization(Singleton orginalsingleton) 
     throws FileNotFoundException, IOException, ClassNotFoundException { 

    /** 
    * Serialization 
    */ 
    ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("E:\\Singleton.ser")); 
    outputStream.writeObject(orginalsingleton); 
    outputStream.close(); 

    /** 
    * DeSerialization 
    */ 
    inputStream = new ObjectInputStream(new FileInputStream("E:\\Singleton.ser")); 

    Singleton deserializeObject = (Singleton) inputStream.readObject(); 
    deserializeObject.hashCode(); 
    printSingletonData("By Serialization", orginalsingleton, deserializeObject); 


} 

public static void printSingletonData(String operationName, 
     Singleton orginalsingleton, Singleton reflectionSigletonObject) { 

    System.out.println("------------------------------------------"); 
    System.out.println("New Operation"); 
    System.out.println(operationName); 
    System.out.println("orginal Hashcode=" + orginalsingleton.hashCode()); 
    System.out.println("New Object hashcode=" 
      + reflectionSigletonObject.hashCode()); 
    Boolean value = orginalsingleton.hashCode() != reflectionSigletonObject.hashCode(); 
    System.out.println("These Object have different hascode. They are two different object Right = " 
        + value); 
    System.out.println("As these are different Object this means Singleton Pattern is broken"); 
} 


private static void printSingletonData(String operationName,Singleton singleton) { 


    System.out.println("------------------------------------------"); 
    System.out.println("New Operation"); 
    System.out.println(operationName); 
    System.out.println("Object hashcode=" + singleton.hashCode()); 
    //System.out.println("As these are different Object this means Singleton Pattern is broken"); 

} 

} 
Cuestiones relacionadas