web-dev-qa-db-ja.com

シングルトン:Reflectionを介したインスタンスの作成を停止する方法

私はJavaで、クラスのインスタンスをnewclone()Reflectionおよび_serializing and de-serializing_で作成できることを知っています。 。

シングルトンを実装する単純なクラスを作成しました。

そして、クラスのインスタンスを作成できる限り停止する必要があります。

_public class Singleton implements Serializable{
    private static final long serialVersionUID = 3119105548371608200L;
    private static final Singleton singleton = new Singleton();
    private Singleton() { }
    public static Singleton getInstance(){
        return singleton;
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {
        throw new CloneNotSupportedException("Cloning of this class is not allowed"); 
    }
    protected Object readResolve() {
        return singleton;
    }
    //-----> This is my implementation to stop it but Its not working. :(
    public Object newInstance() throws InstantiationException {
        throw new InstantiationError( "Creating of this object is not allowed." );
    }
}
_

このクラスでは、newclone()、およびserializationによってクラスインスタンスを停止できましたが、Reflectionによって停止することはできません。

オブジェクトを作成するための私のコードは

_try {
    Class<Singleton> singletonClass = (Class<Singleton>) Class.forName("test.singleton.Singleton");
    Singleton singletonReflection = singletonClass.newInstance();
} catch (ClassNotFoundException e) {
    e.printStackTrace();
} catch (InstantiationException e) {
    e.printStackTrace();
} catch (IllegalAccessException e) {
    e.printStackTrace();
}
_
33

パブリックコンストラクターを作成してみてください

private Singleton() {
    if( Singleton.singleton != null ) {
        throw new InstantiationError( "Creating of this object is not allowed." );
    }
}
55
Dave G

次のようにシングルトンを定義します。

public enum Singleton {
    INSTANCE
}
18
Arne Deutsch

コンストラクターでのチェックインはどうですか:

private Singleton() {
    if (singleton != null) {
        throw new IllegalStateException("Singleton already constructed");
    }
}

もちろん、これは本当にそれを止めないかもしれません-誰かがリフレクションをいじってプライベートメンバーにアクセスする場合、彼らはフィールドをnullに設定できるかもしれません自分自身。しかし、あなたは何を防ごうとしているのか、そしてそれがどれほど価値があるのか​​を自問する必要があります。

(編集:Bozhoが述べたように、最終フィールドはリフレクションを介しても設定できない場合があります。someを介してそれを行う方法があったとしても驚かないでしょうJNIなど...十分なアクセス権を与えると、ほとんど何でもできるようになります...)

14
Jon Skeet
_private Singleton() { 
    if (Singleton.singleton != null) {
        throw new RuntimeException("Can't instantiate singleton twice");
    }
}
_

注目すべきもう1つのことは、readResolve(..)メソッドです。これは、クラスがSerialiableを実装しているためです。そこで、既存のインスタンスを返す必要があります。

しかし、シングルトンを使用する最も簡単な方法は列挙型を使用することです。これらについて心配する必要はありません。

10
Bozho

列挙型ソリューションとは別に、他のすべてはReflexionを介して回避することができますこれらは、Dave Gソリューションを回避する方法の2つの例です。

1:変数Singleton.singletonをnullに設定する

_Constructor<?>[] constructors = Singleton.class.getDeclaredConstructors();
Constructor theConstructor = constructors[0];
theConstructor.setAccessible(true);
Singleton instance1 = (Singleton) theConstructor.newInstance();

Singleton.getInstance();

Field f1 = Singleton.class.getDeclaredField("singleton");
f1.setAccessible(true);
f1.set(f1, null);
Singleton instance2 = (Singleton) theConstructor.newInstance();

System.out.println(instance1);
System.out.println(instance2);
_

出力:

  • シングルトン@ 17f6480
  • シングルトン@ 2d6e8792

2:getInstanceを呼び出していません

_Constructor<?>[] constructors = Singleton.class.getDeclaredConstructors();
Constructor theConstructor = constructors[0];
theConstructor.setAccessible(true);
Singleton instance1 = (Singleton) theConstructor.newInstance();
Singleton instance2 = (Singleton) theConstructor.newInstance();

System.out.println(instance1);
System.out.println(instance2);
_

出力:

  • シングルトン@ 17f6480
  • シングルトン@ 2d6e8792

したがって、Enumを使用したくない場合は2つの方法が考えられます。

最初のオプション:securityManagerの使用:

不正な操作の使用を防ぎます(クラスの外部からプライベートメソッドを呼び出す....)

したがって、他の回答で提案されたシングルトンコンストラクターに1行追加するだけです。

_private Singleton() {
    if (singleton != null) {
        throw new IllegalStateException("Singleton already constructed");
    }
    System.setSecurityManager(new SecurityManager());
}
_

setAccessible(true)を呼び出せないようにするということです。

_Constructor<?>[] constructors = Singleton.class.getDeclaredConstructors();
Constructor theConstructor = constructors[0];
theConstructor.setAccessible(true);
_

この例外は発生します:Java.security.AccessControlException: access denied ("Java.lang.RuntimePermission" "createSecurityManager")

2番目のオプション:シングルトンコンストラクターで、呼び出しがReflexionを介して行われたかどうかをテストします。

呼び出し元のクラスまたはメソッドを取得する最良の方法については、この他の Stackoverflow thread を参照してください。

したがって、これをSingletonコンストラクタに追加すると:

_String callerClassName = new Exception().getStackTrace()[1].getClassName();
System.out.println(callerClassName);
_

そして、私はこれを次のように呼び出します:

_Constructor<?>[] constructors = Singleton.class.getDeclaredConstructors();
Constructor theConstructor = constructors[0];
theConstructor.setAccessible(true);
Singleton instance1 = (Singleton) theConstructor.newInstance();
_

出力は次のようになります:_jdk.internal.reflect.DelegatingConstructorAccessorImpl_

しかし、定期的に呼び出す(パブリックコンストラクターのインスタンス化またはReflexionを使用しないメソッドの呼び出し)場合、呼び出し元のメソッドのクラスの名前が出力されます。だから例えば私は持っています:

_public class MainReflexion {
    public static void main(String[] args) {
        Singleton.getInstance();
    }
}
_

callerClassNameはMainReflexionになるため、出力はMainReflexionになります。


[〜#〜] ps [〜#〜]:提案されたソリューションに回避策が存在する場合はお知らせください

1
ihebiheb

静的なネストされたクラスを使用してそれを壊すことができます

その100%正しいコードを以下に従ってください、私はテストしました

package com.singleton.breakable;

import Java.io.Serializable;

class SingletonImpl implements Cloneable, Serializable {

    public static SingletonImpl singleInstance = null;

    private SingletonImpl() {

    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return singleInstance;
    };

    public Object readResolve() {
        return SingletonImpl.getInstance(); // 
    }

    public static SingletonImpl getInstance() {

        if (null == singleInstance) {
            singleInstance = new SingletonImpl();
        }
        return singleInstance;
    }

}


package com.singleton.breakable;

import Java.io.FileInputStream;
import Java.io.FileOutputStream;
import Java.io.ObjectInputStream;
import Java.io.ObjectOutputStream;
import Java.lang.reflect.Constructor;

class FullySingletonClass {

    public static void main(String[] args) {

        SingletonImpl object1 = SingletonImpl.getInstance();
        System.out.println("Object1:" + object1);

        try {
            FileOutputStream fos = new FileOutputStream("abc.txt");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(object1);

            FileInputStream fis = new FileInputStream("abc.txt");
            ObjectInputStream ois = new ObjectInputStream(fis);
            SingletonImpl object2 = (SingletonImpl) ois.readObject();
            System.out.println("Object2" + object2);

        } catch (Exception e) {
            // TODO: handle exception
        }
        try {
            Constructor[] constructors = SingletonImpl.class.getDeclaredConstructors();
            for (Constructor constructor : constructors) {
                // Below code will not destroy the singleton pattern
                constructor.setAccessible(true);
                SingletonImpl Object3 = (SingletonImpl) constructor.newInstance();
                System.out.println("Object3: Break through Reflection:" + Object3);
                break;
            }
        } catch (Exception ew) {

        }

    }
}

**OUTPUT**
Object1:com.singleton.breakable.SingletonImpl@15db9742
Object2com.singleton.breakable.SingletonImpl@15db9742
Object3: Break through Reflection:com.singleton.breakable.SingletonImpl@33909752

I Thing Belowコードは動作します..

class Test {

    static private Test t = null;
    static {
        t = new Test();
    }

    private Test(){}

    public static Test getT() {
        return t;
    }

    public String helloMethod() {
        return "Singleton Design Pattern";
    }
}


public class MethodMain {

    public static void main(String[] args) {
        Test t = Test.getT();
        System.out.println(t.helloMethod());
    }
}

出力:シングルトンデザインパターン

1
Ashish

シングルトンの代替として、 monostate pattern を見ることができます。そうすれば、クラスのインスタンス化はもう問題にならず、リストしたシナリオのanyを心配する必要はありません。

モノステートパターンでは、クラスのすべてのフィールドはstaticです。つまり、シングルトンの場合と同様に、クラスのすべてのインスタンスが同じ状態を共有します。さらに、この事実は発信者には透過的です。 getInstanceのような特別なメソッドについて知る必要はありません。単にインスタンスを作成し、それらを操作します。

しかし、シングルトンと同様に、それは隠されたグローバルな状態の形式です。これは非常に悪いです。

1
Jordão

リフレクションによって発生する問題を克服するために、Javaは内部的に列挙値が1回だけインスタンス化されることを保証するため、列挙が使用されます。Java唯一の欠点は、柔軟性がないこと、つまり遅延初期化ができないことです。

public enum Singleton {
 INSTANCE
}

public class ReflectionTest 
{

    public static void main(String[] args)
    {
        Singleton instance1 = Singleton.INSTANCE;
        Singleton instance2 = Singleton.INSTANCE;
    System.out.println("instance1 hashcode- "
                                      + instance1.hashCode());
        System.out.println("instance2 hashcode- "
                                      + instance2.hashCode());
    }
}

JVMは、enumコンストラクターの作成と呼び出しを内部的に処理します。列挙型はコンストラクターの定義をプログラムに提供しないため、Reflectionからもそれらにアクセスすることはできません。

詳細については、投稿を参照してください。

0
Swati Gour

遅延初期化のアプローチ:

  private static Singleton singleton;

  public static Singleton getInstance() {
    if(singleton==null){
      singleton= new Singleton();
    }
    return singleton;
  }


private Singleton() {
    if (Singleton.singleton != null) {
      throw new InstantiationError("Can't instantiate singleton twice");
    }
    Singleton.singleton = this;
}

この方法は、getInstanceを呼び出す前にリフレクションを使用してインスタンスを作成する場合でも機能します

0
Nikhil

ただ注意してください、Java 8現在、私のチェックによれば、リフレクションを介してシングルトンをインスタンス化することはできません。

この例外が発生します:

Exception in thread "main" Java.lang.IllegalAccessException: Class com.s.Main can not access a member of class com.s.SingletonInstance with modifiers "private"
at Sun.reflect.Reflection.ensureMemberAccess(Unknown Source)
at Java.lang.Class.newInstance(Unknown Source)
at com.s.Main.main(Main.Java:6)
0
olive_tree

シリアル化、クローン作成、およびリフレクション中のインスタンス作成を回避できる完全なシングルトンクラス。

import Java.io.Serializable;

public class Singleton implements Cloneable, Serializable {

    private static final long serialVersionUID = 1L;
    private static volatile Singleton instance;

    private Singleton() {
        if (instance != null) {
            throw new InstantiationError("Error creating class");
        }
    }

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {

                if (instance == null) {
                    return new Singleton();
                }
            }
        }
        return null;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        throw new CloneNotSupportedException();
    }

    Object readResolve() {
        return Singleton.getInstance();
    }

}
0
ABHAY JOHRI