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