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

        Java的反射破坏单例的私有构造函数保护,最典型的就是Spring的Bean注入,我们可以通过改造私有构造函数来防止。

 

        在Singleton中,我们只对外提供工厂方法(获取单例),而私有化构造函数,来防止外面多余的创建。

        对于一般的外部调用来说,私有构造函数已经很安全了。

    public class Singleton {
        private Singleton(){}  
        private static volatile Singleton instance = null;
        public static Singleton getInstance() throws Exception {
           if (instance == null) {  //为了简洁度,暂不考虑线程安全
               instance = new Singleton();
           }
           return instance;
        }
    }
        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);
       Singleton singleton = (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>

 

----参考《Effective Java》 & 《Spring in Action》

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值