web-dev-qa-db-ja.com

javaシングルトン-リフレクションによる複数の作成を防止します

私はこのようなシングルトンを持っています。

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

これを実行すると、複数の印刷メッセージが表示されました。この反射からこのシングルトンが複数回インスタンス化されるのを防ぐ方法はありますか?

ありがとう。

23
user826323

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)

ほら、誰かがすでに列挙型の答えを出しました。より完全にするためにとにかく投稿します。

16
laher

コンストラクターでアサーションを作成します。

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

遅延読み込みを使用する場合のJavaシングルトン反射プルーフの作成

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

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

例外をスローするBohemianのメソッドは、getInstance()が呼び出される前にクライアントがコンストラクターを反射的に呼び出すことを妨げません。 1つのインスタンスのみが作成されることを保証しますが、これがシングルトンクラスのgetInstance()メソッドによって行われるという保証はありません。

アクセス制御チェックは、この不要なインスタンス化を防ぎます。

9
simpatico

Javaでシングルトンパターンを実装する効率的な方法は何ですか? -列挙型を使用すると、説明している内容が妨げられ、Javaでシングルトンを実装するための推奨される方法です。

4
Brian Roach
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");

}

}
2
Deepak

シングルトンが実際に状態を格納しない場合、最善のオプションはシングルトンを使用しないことです。代わりに、静的な状態のないメソッドとしてファクトリを実装します。

1
DwB