java Singleton - 通过反射防止多重创建

发布于 2024-12-11 20:47:40 字数 887 浏览 0 评论 0原文

我有一个这样的单身人士。

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

当我运行这个程序时,我看到不止一条打印输出消息。有什么方法可以防止此单例从此反射中多次实例化吗?

谢谢。

I have a singleton like this.

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

When I run this, I saw more than one printout message. Is there any way to prevent this singleton from being instantiated more than once from this reflection?

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

薔薇婲 2024-12-18 20:47:40

尝试使用枚举。枚举是好的单例。

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)

哦,看,有人已经给出了枚举答案。无论如何发布以获得更多完整性。

Try using an enum. enums make for good Singletons.

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

You can't create an enum via reflection.

The getInstance() method is superfluous but makes it easier to run your test, throwing the following exception:

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

Oh look, someone already gave the enum answer. Posting anyway for more completeness.

黎夕旧梦 2024-12-18 20:47:40

在构造函数中进行断言:

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

Make the assertion in the constructor:

private BookingFactory() {
    if (instance != null)
        throw new IllegalStateException("Only one instance may be created");
    System.out.println("Object is created.");
}
憧憬巴黎街头的黎明 2024-12-18 20:47:40

改编自使用延迟加载时进行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;
  }

构造函数有一个检查以查看如果调用者可以访问它。正如链接所解释的,需要创建一个允许 Singleton 类本身调用构造函数的策略文件。

Bohemian 抛出异常的方法不会阻止客户端在调用 getInstance() 之前反射性地调用构造函数。尽管它确保只创建一个实例,但不能保证这是由 Singleton 类的 getInstance() 方法完成的。

访问控制检查将防止这种不需要的实例化。

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

The constructor has a check to see if the caller has access to it. As the link explains, a policy file that permits the Singleton class itself to call the constructor will need to be created.

Bohemian's method of throwing an exception does not prevent a client from reflectively calling the constructor before getInstance() is called. Even though it ensures that only one instance gets created, there's no guarantee that this is done by the Singleton class' getInstance() method.

The access control check will prevent this unwanted instantiation.

無心 2024-12-18 20:47:40

我强烈建议阅读 什么是实现Java 中的单例模式? - 使用枚举会阻止您所描述的内容,并且是在 Java 中实现单例的推荐方法。

I highly recommend reading What is an efficient way to implement a singleton pattern in Java? - using an enum prevents what you're describing and is the recommended way to implement a singleton in java.

岁月苍老的讽刺 2024-12-18 20:47:40
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");

}

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

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

}

}
月竹挽风 2024-12-18 20:47:40

如果您的单例实际上并不存储状态,那么您最好的选择是不使用单例。相反,将工厂实现为无静态方法。

If your singleton does not actually store state, then your best option is to not use a singleton. Instead, implement the factory as a static state-free method.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文