2011-10-27 4 views
23

私はこのようなシングルトンを持っています。java Singleton - 反射による複数の作成を防止する

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

これを実行すると、複数の出力メッセージが表示されました。このシングルトンがこのリフレクションから複数回インスタンス化されないようにする方法はありますか?

ありがとうございました。

+3

本当の問題はありますか?プログラマーがそのような問題を抱えている場合、シングルトンをリバースエンジニアリングして設計中に考慮されなかった特別なケースをスイートに組み込むつもりであることを意味します。それが最も素晴らしいアイデアだと言っているわけではありませんが、同時に、これはすべて仮説です。 –

+1

+1本当によく分かりやすい質問、特に[SSCCE](http://pscode.org/sscce.html) – Bohemian

答えて

13

コンストラクタでアサーションを行います。

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

これはスレッドセーフなのでしょうか? – Kaunteya

+1

@Kaunteyaはい、質問でコードとともに使用すると、 'instance'は静的ブロック内に作成され、クラスローディング時に(遅延初期化ではなく)実行され、クラスローダには独自のスレッドセーフティが組み込まれているため、 JVMは、コールコンストラクタを含む、クラスが使用される前にすべての静的初期化が完了することを保証します。 – Bohemian

16

enumをお試しください。列挙型は良いシングルトンを作る。

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

リフレクションを使用して列挙型を作成することはできません。

のgetInstance()メソッドは不要ですが、それが簡単にあなたのテストを実行できるようになり、次の例外をスロー:

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

ああ見て、誰かがすでに列挙答えを与えました。とにかくより完全に投稿する。

+0

私は二重引用符で囲んで投票するだけで行きました。おそらくそうだろう。 –

+0

おそらく、自動的に、singletonという単語が含まれていてenumは含まれていないJava質問に自動的にクローズするはずです;) – laher

+0

enumは継承できません。 Singletonが別のクラス(例えば、java.util.Observable)から継承したい場合があります。 – simpatico

1

シングルトンに実際に状態が保存されていない場合は、シングルトンを使用しないことをお勧めします。代わりに、ファクトリをステートフリーの静的メソッドとして実装します。

8

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

コンストラクタ呼び出し側がそれにアクセスしているかどうかを確認するためのチェックを持っています。リンクが説明するように、シングルトンクラス自体がコンストラクターを呼び出すことを許可するポリシーファイルを作成する必要があります。

例外をスローするボヘミアンの方法では、getInstance()が呼び出される前にクライアントがコンストラクターを反射的に呼び出すことができません。インスタンスが1つしか作成されないことを保証しても、これがSingletonクラスのgetInstance()メソッドによって保証されるわけではありません。

このアクセス制御チェックにより、この不要なインスタンス化が防止されます。あなたはそれを阻止してとても心配だろう、なぜ

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

**テストシングルトン**

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"); 

} 

} 
関連する問題