反射破坏单例的私有构造函数保护

Java的反射破坏单例的私有构造函数保护,最典型的就是Spring的Bean注入,我们可以通过改造私有构造函数来防止。
  在Singleton中,我们只对外提供工厂方法(获取单例),而私有化构造函数,来防止外面多余的创建。
  对于一般的外部调用来说,私有构造函数已经很安全了。
         

public class Singleton {
  private Singleton(){}
  private static volatile Singleton instance = null;
  public static SingletongetInstance() throws Exception {
  if (instance ==null) { //为了简洁度,暂不考虑线程安全
  instance =new Singleton();
  }
  returninstance;
  }
  }

  

  

Class singletonClass = Class.forName("com.jscai.spring.demo.Singleton");
Constructor[] cts =singletonClass.getConstructors();
System.out.println(cts.length)

  一般的外部调用,编译器会校验,直接提示编译错误。而正常的反射也是找不到私有构造函数的,所以上面的输出为0.
  但是一些特权用户可以通过反射来访问私有构造函数,并且实例化:
  

Constructor[] cts = singletonClass.getDeclaredConstructors();
System.out.println(cts.length);
cts[0].setAccessible(true);
Singletonsingleton = (Singleton) cts[0].newInstance();

  

  上述代码首先通过反射的getDeclaredConstructors()来获取所有构造函数(public,protected,default * (package) access, andprivate constructors),当然这个函数会校验调用者的权限。
  此时默认还是不能调用私有构造函数的,还需要把访问权限打开setAccessible(true),就可以访问私有构造函数了,这样破坏了单例的私有构造函数保护。
  如果要防御这样的反射侵入,可以修改构造函数,加上第二次实例化的检查。(上面的getInstance()经过多线程(DoubleCheck)处理后,就不会出现线程冲突来触发这个异常)

private static int cntInstance = 0;
private Singleton()throws Exception {
if (++cntInstance > 1 ) {
throw new Exception("Can‘tcreate another instance.");
        }
 }

  

  另外,在Spring的Bean注入中,即使你私有化构造函数,默认他还是会去调用你的私有构造函数去实例化。 【通过BeanFactory来装配Bean,和上面的逻辑如出一辙】
  所以,如果我们想保证实例的单一性,就要在定义<bean>时加上factory-method=""的属性,并且在私有构造函数中添加防御机制。单例的getInstance()可能会添加一些逻辑,而Spring的默认调用构造函数去创建,就不能保证这份逻辑的准确性,所以会带来隐患。
  我们可以通过scope="prototype"来测试单例是否被多次创建:

  <beanid="test"class="com.jscai.spring.demo.Singleton"scope="prototype"></bean>
  BeanFactory bf = new ClassPathXmlApplicationContext("demoAppTestContext.xml");
  Singleton test1 = (Singleton) bf.getBean("singleton");
  Singleton test2 = (Singleton) bf.getBean("singleton");

  

  发现防御机制生效,抛出"Can‘t create another instance."的异常,证明Spring能正常调用私有的构造函数来创建Bean,并且创建了多次。
  这时候我们要使用factory-method来指定工厂方法,才能达到我们想要的效果
  

<beanid="test"class="com.jscai.spring.demo.Singleton"scope="prototype"factory-method="getInstance"></bean>

  

时间: 2024-07-30 16:45:50

反射破坏单例的私有构造函数保护的相关文章

反射破坏单例

class SingLeton{ private static SingLeton inntleton = null; private ArrayList stlit = null; //私有构造函数 private SingLeton() { } public static SingLeton singl() { if (inntleton == null) { inntleton = new SingLeton(http://www.amjmh.com/v/); } return inntl

设计模式 - 单例模式之多线程调试与破坏单例

前言 在之前的 设计模式 - 单例模式(详解)看看和你理解的是否一样? 一文中,我们提到了通过Idea 开发工具进行多线程调试.单例模式的暴力破坏的问题:由于篇幅原因,现在单独开一篇文章进行演示:线程不安全的单例在多线程情况下为何被创建多个.如何破坏单例. 如果还不知道如何使用IDEA工具进行线程模式的调试,请先阅读我之前发的一篇文章: 你不知道的 IDEA Debug调试小技巧 一.线程不安全的单例在多线程情况下为何被创建多个 首先回顾简单线程不安全的懒汉式单例的代码以及测试程序代码: /**

类和对象的方法与属性---懒加载与私有事件---单例与私有化构造函数

对象属性: 1.private修饰的属性:只能在本类内部访问,分类和外部都不能访问(彻底私有) 2.fileprivate修饰的属性:在本类和分类中可以访问,外部不能访问(部分私有) 3.直接用let或var修饰的属性:在本类.分类和外部都可以访问(开放) 4.在分类中只能声明计算属性,不能声明存储属性 1 // 属性:可以被外界访问 2 var name:String? 3 4 // private修饰的属性:只能在本类内部访问,分类和外部都不能访问 5 private var age:Str

如何防止JAVA反射对单例类的攻击?

加静态标志位,构造方法通过synchronized修饰.或者枚举 举例1:不经过处理的单例类被JAVA反射机制攻击 Singleton.java    代码清单[1.1] 1 public class Singleton 2 { 3 private static boolean flag = true; 4 private static final Singleton INSTANCE = new Singleton(); 5 6 private Singleton() 7 { 8 } 9 10

Java单例---反射攻击单例和解决方法

静态内部类中引出了反射攻击的问题 import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class Test1 { public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, In

JS单例对象与构造函数对象的区别

JavaScript对象有几种: 内置对象如Global,Math对象等等. 本地对象如Object.Function.Array.String.Boolean.Number.Date.RegExp.Error.EvalError.RangeError.ReferenceError.SyntaxError.TypeError.URIError. 然而这跟要说的内容并没有什么关系,正题:曾经(此秒之前)纠结过json对象和构造函数对象有什么关系,区别是什么,该怎么合理使用,今天心情...仔细思考探

Java反射破坏单例模式

今天电话面试的时候问到了,Google了一下 原出处: http://blog.csdn.net/lws332969674/article/details/8125893 一. Java中的反射技术可以获取类的所有方法.成员变量.还能访问private的构造方法,这样一来,单例模式中用的私有构造函数被调用就会产生多个实例,编写代码测试一下. [java] view plaincopyprint? package test; import java.lang.reflect.Constructor

java单例之enum实现方式

传统的两私有一公开(私有构造方法.私有静态实例(懒实例化/直接实例化).公开的静态获取方法)涉及线程安全问题(即使有多重检查锁也可以通过反射破坏单例), 目前最为安全的实现单例的方法是通过内部静态enum的方法来实现,因为JVM会保证enum不能被反射并且构造器方法只执行一次. 实现方法如下: /** * 使用枚举的单例模式 * * @author yzl * @see [相关类/方法](可选) * @since [产品/模块版本] (可选) */ public class EnumSingle

设计模式:单例

传统的实现方法:两私一公,涉及线程安全问题(即使有多重检查锁也可以通过反射破坏单例) public class Singleton { private volatile static Singleton instance = null; private Singleton () { } public static Singleton getSingleton() { if(instance == null) { synchronized(Singleton.class) { if(instanc