webentwicklung-frage-antwort-db.com.de

Singleton: So beenden Sie das Erstellen der Instanz über Reflection

Ich weiß, dass wir in Java eine Instanz einer Klasse über new, clone(), Reflection und serializing and de-serializing erstellen können.

Ich habe eine einfache Klasse erstellt, die ein Singleton implementiert.

Und ich muss so lange aufhören, wie man eine Instanz meiner Klasse erstellen kann.

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

In dieser Klasse ist es mir gelungen, die Klasseninstanz durch new, clone() und serialization zu stoppen. Ich kann sie jedoch nicht durch Reflection stoppen.

Mein Code zum Erstellen des Objekts lautet

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

Versuchen Sie, Ihren öffentlichen Konstruktor zu erstellen

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

Definieren Sie das Singleton wie folgt:

public enum Singleton {
    INSTANCE
}
15
Arne Deutsch

Wie wäre es mit dem Einchecken des Konstruktors:

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

Natürlich darf dies nicht wirklich aufhören - wenn jemand mit Überlegungen herumspielt, um auf private Mitglieder zuzugreifen, kann er das Feld möglicherweise selbst auf Null setzen. Sie müssen sich nur fragen, was Sie verhindern wollen und wie lohnend es ist.

(BEARBEITEN: Wie Bozho bereits erwähnt hat, sind endgültige Felder möglicherweise nicht einmal über Reflexion einstellbar. Es würde mich nicht wundern, wenn es einige Möglichkeiten gäbe, dies über JNI usw. zu tun ... wenn Sie den Leuten genug geben Zugang, sie werden in der Lage sein, fast alles zu tun ...)

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

Eine andere Sache, die Sie beachten sollten, ist die readResolve(..)-Methode, da Ihre Klasse Serialiable implementiert. Dort sollten Sie die vorhandene Instanz zurückgeben.

Die einfachste Art, Singletons zu verwenden, ist jedoch die Verwendung von Enummen.

10
Bozho

Wir können es mit statischen verschachtelten Klassen brechen 

Bitte folgen Sie dem Code, der zu 100% korrekt ist, habe ich getestet

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:[email protected]
[email protected]
Object3: Break through Reflection:[email protected]

I Thing Below Code wird funktionieren ..

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

ausgabe: Singleton Design Pattern

1
Ashish

Als Alternative zum Singleton können Sie das Monostate-Pattern betrachten. Dann ist die Instantiierung Ihrer Klasse kein Problem mehr und Sie müssen sich keine Gedanken über irgendein der von Ihnen aufgelisteten Szenarien machen.

Im monostatischen Muster sind alle Felder in Ihrer Klasse static. Das bedeutet, dass alle Instanzen der Klasse den gleichen Status aufweisen, wie bei einem Singleton. Darüber hinaus ist diese Tatsache für die Anrufer transparent. Sie müssen keine speziellen Methoden wie getInstance kennen, sie erstellen einfach Instanzen und arbeiten mit ihnen.

Aber genau wie bei Singleton ist es eine Form eines verborgenen globalen Zustands. das ist sehr schlecht.

1
Jordão

Abgesehen von der Enum-Lösung können alle anderen über Reflexion bearbeitet werden. Dies sind zwei Beispiele, wie Sie die Dave G-Lösung umgehen können:

1: Setzen der Variablen Singleton.singleton auf 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);

Ausgabe :

  • Singleton @ 17f6480 
  • Singleton @ 2d6e8792

2: getInstance nicht aufrufen

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

Ausgabe :

  • Singleton @ 17f6480 
  • Singleton @ 2d6e8792

Ich kann mir also zwei Möglichkeiten einfallen, wenn Sie nicht mit einem Enum gehen wollen:

1. Option: Mit dem securityManager:

Es verhindert die Verwendung nicht autorisierter Operationen (Aufruf privater Methoden von außerhalb der Klasse ...)

Sie müssen also nur eine Zeile zu dem von den anderen Antworten vorgeschlagenen Singleton-Konstruktor hinzufügen 

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

was es tut, ist, dass es den Aufruf von setAccessible(true)So verhindert, wenn Sie es aufrufen möchten:

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

diese Ausnahme wird auftreten: Java.security.AccessControlException: access denied ("Java.lang.RuntimePermission" "createSecurityManager") 

2. Option: Testen Sie im Singleton-Konstruktor, ob der Aufruf über Reflexion erfolgt: 

Ich verweise Sie auf diesen anderen Stackoverflow-Thread , um die aufrufende Klasse oder Methode am besten zu ermitteln. 

Wenn ich dies im Singleton-Konstruktor hinzufüge:

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

Und ich nenne es so:

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

die Ausgabe wird sein: jdk.internal.reflect.DelegatingConstructorAccessorImpl

wenn ich es jedoch regelmäßig anrufe (einen öffentlichen Konstruktor instantiieren oder eine Methode ohne Reflexion aufrufen), wird der Name der Klasse der aufrufenden Methode ausgegeben. So habe ich zum Beispiel: 

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

der callerClassName ist MainReflexion und die Ausgabe ist MainReflexion.


PS: Falls für die vorgeschlagenen Lösungen Workarounds vorhanden sind, lassen Sie es mich wissen

0
ihebiheb

Um das durch Nachdenken hervorgerufene Problem zu überwinden, werden Aufzählungen verwendet, da Java intern dafür sorgt, dass der Aufzählungswert nur einmal instantiiert wird. Da Java Enums global zugänglich sind, können sie für Singletons verwendet werden. Sein einziger Nachteil ist, dass es nicht flexibel ist, d. H. Es keine faule Initialisierung erlaubt.

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

Die JVM übernimmt die interne Erstellung und das Aufrufen von Enum-Konstruktoren. Da Enums dem Konstruktor keine Konstruktordefinition geben, können wir auch nicht über Reflection darauf zugreifen.

Weitere Informationen finden Sie in der Post.

0
Swati Gour

Ansatz mit Lazy-Initialisierung:

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

Dieser Ansatz funktioniert auch, wenn Sie vor dem Aufruf von getInstance eine Instanz mit Reflektion erstellen

0
Nikhil

Nur um zu bemerken, dass Sie ab Java 8 und meiner Kontrolle zufolge nicht über Reflections ein Singleton instanziieren können, solange es einen privaten Konstruktor hat.

Sie erhalten diese Ausnahme:

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

Perfekte Singleton-Klasse, die die Instanzerstellung während der Serialisierung, des Klonens und der Reflektion verhindert.

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