设计模式:单例模式(3)

2019-07-23  本文已影响0人  谁家的猪

反射攻击解决方案及原理

测试反射是否可以创建单例对象

  1. 创建测试类
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * @author lijiayin
 */
public class ReflectTest {

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class clazz = HungrySingleton.class;
        Constructor constructor = clazz.getDeclaredConstructor();
        constructor.setAccessible(true);
        HungrySingleton instance = HungrySingleton.getInstance();
        HungrySingleton object = (HungrySingleton) constructor.newInstance();
        System.out.println(instance);
        System.out.println(object);
        System.out.println(instance == object);
    }
}
  1. 测试结果


    测试结果.png
  2. 结论
    通过反射可以创建对象

反射攻击解决方案

构造器抛出异常,只适用于通过类初始化实现单例的模式

  1. 修改HungrySingleton类
import java.io.Serializable;

/**
 * @author lijiayin
 */
public class HungrySingleton implements Serializable {
    private final static HungrySingleton hungrySingleton;
    static {
        hungrySingleton = new HungrySingleton();
    }
    private HungrySingleton(){
        if(hungrySingleton != null){
            throw new RuntimeException("单例构造器禁止反射调用");
        }
    }
    public static HungrySingleton getInstance(){
        return hungrySingleton;
    }
    private Object readResolve(){
        return hungrySingleton;
    }
}
  1. 测试结果


    测试结果.png
  2. 结论
    成功阻止反射创建
  3. 同理StaticInnerClassSingleton这种单例方法,也可以通过这种方式阻止反射调用
/**
 * @author lijiayin
 */
public class StaticInnerClassSingleton {
    
    private StaticInnerClassSingleton(){
        if(InnerClass.staticInnerClassSingleton != null){
            throw new RuntimeException("单例构造器禁止反射调用");
        }
    }
    
    private static class InnerClass {
        private static StaticInnerClassSingleton staticInnerClassSingleton = new StaticInnerClassSingleton();
    }
    
    public static StaticInnerClassSingleton getInstance(){
        return InnerClass.staticInnerClassSingleton;
    }
}

使用枚举类

创建EnumInstance枚举类型

/**
 * @author lijiayin
 */
public enum EnumInstance {
    
    INSTANCE;
    private Object data;

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public static EnumInstance getInstance(){
        return INSTANCE;
    }
}

测试序列化与反序列化

代码实现
import java.io.*;

/**
 * @author lijiayin
 */
public class EnumSerializableTest {

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        EnumInstance enumInstance = EnumInstance.getInstance();
        enumInstance.setData(new Object());
        ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("singleton_file"));
        outputStream.writeObject(enumInstance);
        
        File file = new File("singleton_file");
        ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(file));
        EnumInstance newInstance = (EnumInstance)inputStream.readObject();
        System.out.println(enumInstance.getData());
        System.out.println(newInstance.getData());
        System.out.println(enumInstance.getData() == newInstance.getData());
    }
}
测试结果
测试结果.png
原理

ObjectInputStream的源码,readObject()方法,与上一章类似,这次由于是enum类型,调用readEnum()方法,并没有创建新对象。

测试反射

代码实现
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * @author lijiayin
 */
public class EnumReflectTest {

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class clazz = EnumInstance.class;
        Constructor constructor = clazz.getDeclaredConstructor(String.class, Integer.class);
        constructor.setAccessible(true);
        EnumInstance enumInstance = (EnumInstance) constructor.newInstance("ABC", 888);
    }
}
测试结果
测试结果.png
上一篇 下一篇

猜你喜欢

热点阅读