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);

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

优化DataHandlerFactory类

import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Constructor;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
public final 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);
            //设置允许访问,相当于让privite关键字失效。让构造函数允许被newInstance静态方法以外的方式进行实例化。
            cachedConstructor.setAccessible(true);
        } catch (NoSuchMethodException | SecurityException e) {
            log.error(e.getMessage(), e);
            throw new RuntimeException(e);
        }
    }

    public static DataHandler createNewInstance(String appId, String appSecret, String publicKey, String privateKey) {
    	//缓存键
    	String cacheKey = appId + appSecret + publicKey + privateKey;
        // 如果缓存没有该缓存键,则创建并返回实例。如果有,则直接返回。
        return instanceCache.computeIfAbsent(cacheKey, k -> {
            try {
                return cachedConstructor.newInstance(appId, appSecret, publicKey, privateKey);
            } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
            	log.error(e.getMessage(), e);
                return null;
            }
        });
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值