您当前的位置: 首页 >  spring

java持续实践

暂无认证

  • 2浏览

    0关注

    746博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

Spring 源码解析之 防止反射和序列化破解的单例写法

java持续实践 发布时间:2021-03-07 11:09:26 ,浏览量:2

文章目录
      • 使用枚举内部类的单例
      • 尝试直接通过枚举反射创建实例
      • 反编译查看枚举单例模式源码(证明其是饿汉模式)

使用枚举内部类的单例

如下的类, 使用枚举的内部类方式创建单例. 在枚举中的构造方法创建对象 .

public class EnumStaringSingleton {

    // 私有无参构造函数
    private EnumStaringSingleton() {
    }

    public static EnumStaringSingleton getInstance() {
        return ContainerHolder.HOLDER.instance;
    }

    private enum ContainerHolder {
        // 保存单例对象
        HOLDER;
        private EnumStaringSingleton instance;

        // 枚举的构造方法, 加载的时候就创建出对象, 饿汉式写法
        ContainerHolder() {
            instance = new EnumStaringSingleton();
        }
    }

}

测试 编写如下的测试, 分别通过类名调用静态方法, 和通过反射来创建对象.

   @Test
    public void EnumStaringSingletonTest() throws Exception {
        // 通过调用静态方法创建对象
        System.out.println(EnumStaringSingleton.getInstance());

        // 通过反射创建对象
        Class clazz = EnumStaringSingleton.class;
        Constructor constructor = clazz.getDeclaredConstructor();
        constructor.setAccessible(true);

        EnumStaringSingleton singleton =  constructor.newInstance();
        System.out.println(singleton.getInstance());
    }

结果如下, 可以看到是两个一样的对象.

尝试直接通过枚举反射创建实例

在EnumStaringSingleton类中, 写如下的main方法, 该方法内直接通过获取枚举的class对象, 来创建实例.

  public static void main(String[] args) throws Exception{
        Class clazz = ContainerHolder.class;
        Constructor constructor = clazz.getDeclaredConstructor();
        constructor.setAccessible(true);
        System.out.println(EnumStaringSingleton.getInstance());
        System.out.println(constructor.newInstance());
    }

运行后 , 报异常如下 java.lang.NoSuchMethodException 在这里插入图片描述 原因是枚举中, 没有无参构造函数, 只有一个带参数的构造函数, String . int 在这里插入图片描述 将代码改成如下的, 带参数的 在这里插入图片描述 运行后依旧报错, java.lang.IllegalArgumentException: Cannot reflectively create enum objects , 说明了不能通过 反射的形式, 创建枚举的对象. 说明了枚举是无法通过反射的方式去破解的 在这里插入图片描述

反编译查看枚举单例模式源码(证明其是饿汉模式)

jad 来反编译. 下载网址 https://varaneckas.com/jad/ 根据不同的操作系统进行下载. 我是windows ,因此下载第一个.虽然上面标着是要在intel平台上, 但是 我的CPU是AMD也能用. 下载完成后 ,解压, 可以看到jad.exe 要反编译EnumStaringSingleton文件, 那么首先要生成.class文件. 使用javac命令进行生成. 执行如下的名称, 前面为文件的路径. D:\mycode\spring_study\simpleframework\src\main\java\demo\pattern>javac EnumStaringSingleton.java 执行编译的时候有报错, 错误: 编码 GBK 的不可映射字符 (0xB0), 比较快的解决方式是去除中文注释.

再用jad进行反编译如下: 先传入jad.exe的路径, 再传入class文件的路径.

D:\mycode\spring_study\simpleframework\src\main\java\demo\pattern>D:\softpack\jad158g.win\jad.exe EnumStaringSingleton.class
Parsing EnumStaringSingleton.class...Parsing inner class EnumStaringSingleton$ContainerHolder.class... Generating EnumStaringSingleton.jad

会生成一个如下的jad文件 在这里插入图片描述 内容如下

// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3) 
// Source File Name:   EnumStaringSingleton.java

package demo.pattern;


public class EnumStaringSingleton
{
    private static final class ContainerHolder extends Enum
    {

        public static ContainerHolder[] values()
        {
            return (ContainerHolder[])$VALUES.clone();
        }

        public static ContainerHolder valueOf(String s)
        {
            return (ContainerHolder)Enum.valueOf(demo/pattern/EnumStaringSingleton$ContainerHolder, s);
        }

        public static final ContainerHolder HOLDER;
        private EnumStaringSingleton instance;
        private static final ContainerHolder $VALUES[];

        static 
        {
            HOLDER = new ContainerHolder("HOLDER", 0);
            $VALUES = (new ContainerHolder[] {
                HOLDER
            });
        }

        private ContainerHolder(String s, int i)
        {
            super(s, i);
            instance = new EnumStaringSingleton();
        }
    }


    private EnumStaringSingleton()
    {
    }

    public static EnumStaringSingleton getInstance()
    {
        return ContainerHolder.HOLDER.instance;
    }
}

通过上面的内容可以看到 , 枚举ContainerHolder, 反编译后, 本质是一个静态类, 去继承了枚举 private static final class ContainerHolder extends Enum 创建单例对象是如下的静态代码块 . 说明了枚举的单例是饿汉模式.

static 
        {
            HOLDER = new ContainerHolder("HOLDER", 0);
            $VALUES = (new ContainerHolder[] {
                HOLDER
            });
        }

破解单例模式还有序列化与反序列的方式, 在反序列的时候, 再创建一个单例.

关注
打赏
1658054974
查看更多评论
立即登录/注册

微信扫码登录

0.0459s