前言
在博客上看到一篇《防止单例模式被JAVA反射攻击》的文章,通过一个静态布尔变量记录下单例类是否是第一次初始化,然后在构造函数内抛出异常来防止反射破坏。看起来合情合理,但细想,通过反射来修改那个静态变量,再调用构造函数进行实例化,同样可以破坏。按照上面的原理,我换了另一种方式实现单例类,并进行破坏。下面进行代码演示。
代码实现
单例类:
public class SingletonProPro {
private static SingletonProPro instance;
private SingletonProPro(){
if(instance!=null){
throw new RuntimeException("单例类已拦截入侵");
}
}
public static SingletonProPro getInstance(){
if(instance==null){
synchronized(SingletonProPro.class){
if(instance==null){
instance=new SingletonProPro();
}
}
}
return instance;
}
}
攻击代码:
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public class Main {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
//通过调用getSingleton()方式获取对象
SingletonProPro instance1 = SingletonProPro.getInstance();
//通过反射方式获取对象
Class singletonProClass = instance1.getClass();
SingletonProPro instance2 = null;
//修改instance为null
Field flag = singletonProClass.getDeclaredField("instance");
flag.setAccessible(true);
System.out.println(flag.get(instance1));
flag.set(instance1,null);
System.out.println(flag.get(instance1));
Constructor<?> constructor = singletonProClass.getDeclaredConstructor();//获取当前Class所表示类中指定的一个的构造器,和访问权限无关
constructor.setAccessible(true); //设置私有方法的可访问(切记,这里必须设置,否则会抛出下图的异常)
instance2 = (SingletonProPro) constructor.newInstance();
if(instance1==instance2){
System.out.println("相等");
}else {
System.out.println("不相等");
}
}
}
输出结果:
SingletonProPro@14ae5a5
null
不相等
防止反射破坏单例
最简单的方法就是用static final来修饰instance并初始化:
public class SingletonPro {
private static final SingletonPro instance = new SingletonPro();
private SingletonPro(){
if(instance!=null){
throw new RuntimeException("单例模式被侵犯!");
}
}
public static SingletonPro getInstance(){
return instance;
}
}