java解决调用死环导致栈溢出
场景:最近写了个StringUtils,继承了org.apache.commons.lang3.StringUtils,在这个蛮好用的字符串工具类上做一些扩展,扩展到toString的时候,我写了一个safeToString方法,传入一个对象,无论你传的是啥都能给你变打印字符串并且不报错,问题就遇到了,假设我对象自己重写了toString方法,我当然要用对象自己的toString,那么我就写了,但是出现了一个问题,就是,如果对象重写的toString里调用的我们这个工具类的safeToString去打印,那就会出现栈溢出,那就需要破解这个死环,所以设计了一个modMap来检测出调用环
import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 字符串工具类
* 继承org.apache.commons.lang3.StringUtils
*
* @author humorchen
* @date 2022/8/11 16:20
*/
public class StringUtils extends org.apache.commons.lang3.StringUtils {
/**
* urf8编码
*/
public static final String UTF8 = "utf8";
/**
* 空字符串
*/
public static final String EMPTY_STRING = "";
/**
* null的字符串
*/
public static final String NULL_STRING = "null";
/**
* 默认字符串编码
*/
public static final String DEFAULT_CHARSET = UTF8;
/**
* 防止死循环
*/
private static final Map<Object, Integer> modMap = new ConcurrentHashMap<>();
/**
* 日志
*/
private static Logger logger = LoggerFactory.getLogger(StringUtils.class);
/**
* 压缩成url编码的字符串(默认utf8)
*
* @param str
* @return
*/
public static String urlEncode(String str) {
return urlEncode(str, DEFAULT_CHARSET);
}
/**
* 压缩成url编码的字符串
*
* @param str
* @param charset
* @return
*/
public static String urlEncode(String str, String charset) {
if (isEmpty(str)) {
return EMPTY_STRING;
}
String ret = EMPTY_STRING;
try {
ret = URLEncoder.encode(str, charset);
} catch (UnsupportedEncodingException e) {
logger.error("urlEncode error", e);
}
return ret;
}
/**
* url字符串解码(默认utf8)
*
* @param str
* @return
*/
public static String urlDecode(String str) {
return urlDecode(str, DEFAULT_CHARSET);
}
/**
* url字符串解码
*
* @param str
* @param charset
* @return
*/
public static String urlDecode(String str, String charset) {
if (isEmpty(str)) {
return EMPTY_STRING;
}
String ret = EMPTY_STRING;
try {
ret = URLDecoder.decode(str, charset);
} catch (UnsupportedEncodingException e) {
logger.error("urlDecode error", e);
}
return ret;
}
/**
* 安全序列化
*
* @param obj
* @return
*/
public static String safeToString(Object obj) {
if (obj == null) {
return NULL_STRING;
}
String ret = EMPTY_STRING;
boolean mod = false;
try {
// 重写了且无调用死环就用自己的
if (isOverrideToString(obj) && modMap.put(obj, 1) == null) {
mod = true;
ret = obj.toString();
} else {
// 默认json输出
ret = JSONObject.toJSONString(obj);
}
} catch (Exception e) {
logger.error("safeToString error", e);
} finally {
if (mod) {
modMap.remove(obj);
}
}
return ret;
}
/**
* 是否重写了toString方法
*
* @param classInstance
* @return
*/
public static boolean isOverrideToString(Object classInstance) {
return classInstance != null && isOverrideToString(classInstance.getClass());
}
/**
* 是否重写了toString方法
* (用toString方法和@Overrided判定不准确)
*
* @param cls
* @return
*/
public static boolean isOverrideToString(Class<?> cls) {
// 空的和根对象不算重写
if (cls == null || Object.class.equals(cls)) {
return false;
}
// 是否自己声明了toString方法
Method[] declaredMethods = cls.getDeclaredMethods();
for (Method method : declaredMethods) {
if ("toString".equals(method.getName()) && method.getReturnType().equals(String.class) && method.getParameterTypes().length == 0) {
return true;
}
}
return false;
}
}
本文来自博客园,作者:HumorChen99,转载请注明原文链接:https://www.cnblogs.com/HumorChen/p/18039480