当前位置: 首页 > news >正文

Java通过反射破坏单例模式

有个第三方工具类,不支持多例模式。但是又不能直接改第三方工具类的代码,因此可以通过反射破坏第三方工具类的单例。

第三方工具类反编译如下

可以看到构造函数进行了私有化,不允许外部new,只能通过newInstance进行实例化。并且newInstance是单例的。
DataHandler.class

public class DataHandler {private static volatile DataHandler INSTANCE;private final byte[] encryptKeys;private final String publicKeys;private final String privateKeys;private final String appID;private final String secret;private String version = "2.0.0";private boolean skipVerify = false;private DataHandler(String appID, String secret, String publicKey, String privateKey) throws IOException {//.....}public static DataHandler newInstance(String appID, String secret, String publicKey, String privateKey) throws IOException {if (INSTANCE == null) {Class var4 = DataHandler.class;synchronized(DataHandler.class) {if (INSTANCE == null) {INSTANCE = new DataHandler(appID, secret, publicKey, privateKey);}}}return INSTANCE;}
}

通过反射破坏单例模式

import java.lang.reflect.Constructor;public class DataHandlerFactory {public static DataHandler createNewInstance(String appId, String appSecret, String publicKey, String privateKey) {try {// 获取DataHandler类的构造函数Constructor<DataHandler> constructor = DataHandler.class.getDeclaredConstructor(String.class, String.class, String.class, String.class);constructor.setAccessible(true);// 通过反射创建新的实例return constructor.newInstance(appId, appSecret, publicKey, privateKey);} catch (Exception e) {return null;}}}

使用方法

//原本使用第三方工具类的方式,默认单例模式
DataHandler dataHandler = DataHandler.newInstance(appId, appSecret, publicKey, privateKey);//修改后,允许多例。createNewInstance方法如果抛出异常,那么是有可能为null,因此下面要进行手动判空
DataHandler dataHandler = DataHandlerFactory.createNewInstance(appId, appSecret, publicKey, privateKey);
if (Objects.isNull(dataHandler)) {throw new RuntimeException("创建DataHandler对象失败,请检查appId, appSecret, publicKey, privateKey值是否正确。");
}

通过缓存构造函数、缓存重复的实例进一步提升性能和减少反射所带来的性能开销

优化DataHandlerFactory类

import lombok.extern.slf4j.Slf4j;import java.lang.reflect.Constructor;
import java.util.concurrent.ConcurrentHashMap;@Slf4j
public class DataHandlerFactory {// 使用线程安全的 ConcurrentHashMap 存储实例public static final ConcurrentHashMap<String, DataHandler> instanceCache = new ConcurrentHashMap<>();// 缓存构造函数private static Constructor<DataHandler> cachedConstructor;static {try {cachedConstructor = DataHandler.class.getDeclaredConstructor(String.class, String.class, String.class, String.class);cachedConstructor.setAccessible(true);} catch (NoSuchMethodException | SecurityException e) {log.error(e.getMessage(), e);}}public static DataHandler createNewInstance(String appId, String appSecret, String publicKey, String privateKey) {// 创建一个缓存键String cacheKey = appId + "_" + appSecret + "_" + publicKey + "_" + privateKey;// 检查缓存中是否已有该实例,如果有则直接返回DataHandler instance = instanceCache.get(cacheKey);if (instance != null) {return instance;}// 如果缓存中没有该实例,则创建并缓存try {instance = cachedConstructor.newInstance(appId, appSecret, publicKey, privateKey);// 存入缓存,并确保线程安全instanceCache.put(cacheKey, instance);return instance;} catch (Exception e) {log.error(e.getMessage(), e);return null;}}}

http://www.mrgr.cn/news/80649.html

相关文章:

  • 【Java基础面试题014】for循环与增强for的区别是什么?
  • .NET Core 各版本特点、差异及适用场景详解
  • Linux内核 -- 唤醒 `wait_event_interruptible` 等待状态的方案
  • 加速科技精彩亮相ICCAD 2024
  • Unity Pico 实现离线TTS
  • 【HarmonyOS NEXT】ArkTs函数、类、接口、泛型、装饰器解析与使用
  • Compose IO
  • Linux介绍与安装CentOS 7操作系统
  • JS实现简单的前端分页功能
  • Avalonia 开发环境准备
  • asp.net core发布配置端口号,支持linux
  • 使用qemu搭建armv7嵌入式开发环境
  • matlab绘图时设置左、右坐标轴为不同颜色
  • JVM性能优化一:初识内存泄露-内存溢出-垃圾回收
  • Linux Shell 脚本编程基础知识篇
  • 【蓝桥杯】46195.水仙花数
  • ARM学习(38)多进程多线程之间的通信方式
  • Visual Studio 2022 QT5.14.2 新建项目无法打开QT的ui文件,出现闪退情况
  • [spring]XML标签<bean>的二级标签
  • WPF ControlTemplate 控件模板
  • 【递归,搜索与回溯算法】穷举 vs 暴搜 vs 深搜 vs 回溯 vs 剪枝算法入门专题详解
  • “年轻科技旗舰”爱玛A7 Plus正式发布,全国售价4999元
  • AMS1117芯片驱动电路·降压芯片的驱动电路详解
  • linux - 软硬链接
  • Linux -- 线程控制相关的函数
  • C语言栈和队列