package cn.mb.cloud.common.sentinel.fallback;
|
|
import cn.mb.cloud.common.core.constant.enums.ErrorCodeConstants;
|
import cn.mb.cloud.common.core.util.ResponseData;
|
import org.slf4j.Logger;
|
import org.slf4j.LoggerFactory;
|
import org.springframework.cglib.proxy.MethodInterceptor;
|
import org.springframework.cglib.proxy.MethodProxy;
|
|
import java.lang.reflect.Method;
|
import java.lang.reflect.ParameterizedType;
|
import java.lang.reflect.Type;
|
import java.util.ArrayList;
|
import java.util.HashMap;
|
import java.util.HashSet;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.Set;
|
|
/**
|
* 公共 Sentinel Feign 降级处理的安全替代实现。
|
*
|
* <p>依赖版本假定所有泛型返回类型均为 {@link Class}。对于
|
* {@code Map<String, Object>} 等声明并不成立,其运行时类型为
|
* {@link ParameterizedType};此前转换失败会以 AssertionError 掩盖原始下游异常。</p>
|
*/
|
public class DefaultFeignFallback<T> implements MethodInterceptor {
|
private static final Logger log = LoggerFactory.getLogger(DefaultFeignFallback.class);
|
|
private final Class<T> targetType;
|
private final String targetName;
|
private final Throwable cause;
|
|
public DefaultFeignFallback(Class<T> targetType, String targetName, Throwable cause) {
|
this.targetType = targetType;
|
this.targetName = targetName;
|
this.cause = cause;
|
}
|
|
@Override
|
public Object intercept(Object object, Method method, Object[] arguments, MethodProxy methodProxy) {
|
log.error("FunFeignFallback:[{}.{}] serviceId:[{}] message:[{}]",
|
targetType.getName(), method.getName(), targetName, cause == null ? null : cause.getMessage());
|
|
Type declaredReturnType = method.getGenericReturnType();
|
Class<?> returnType = rawType(declaredReturnType);
|
Object data = defaultValue(returnType);
|
|
// 仅显式声明返回 ResponseData 的方法可以返回 ResponseData 降级结果。
|
// 在 Map/List 方法中返回该对象会导致 Feign 代理再次抛出 ClassCastException。
|
if (ResponseData.class.equals(returnType)) {
|
Type dataType = firstTypeArgument(declaredReturnType);
|
return ResponseData.builder()
|
.code(ErrorCodeConstants.FAIL.getValue())
|
.msg("系统降级,请稍后再试!")
|
.data(defaultValue(rawType(dataType)))
|
.build();
|
}
|
return data;
|
}
|
|
private Type firstTypeArgument(Type type) {
|
if (type instanceof ParameterizedType) {
|
Type[] arguments = ((ParameterizedType) type).getActualTypeArguments();
|
if (arguments.length > 0) return arguments[0];
|
}
|
return null;
|
}
|
|
private Class<?> rawType(Type type) {
|
if (type instanceof Class<?>) return (Class<?>) type;
|
if (type instanceof ParameterizedType) return rawType(((ParameterizedType) type).getRawType());
|
return Object.class;
|
}
|
|
private Object defaultValue(Class<?> type) {
|
if (List.class.isAssignableFrom(type)) return new ArrayList<Object>();
|
if (Set.class.isAssignableFrom(type)) return new HashSet<Object>();
|
if (Map.class.isAssignableFrom(type)) return new HashMap<Object, Object>();
|
if (Boolean.class.equals(type) || boolean.class.equals(type)) return Boolean.FALSE;
|
if (type.isPrimitive()) return primitiveDefault(type);
|
return null;
|
}
|
|
private Object primitiveDefault(Class<?> type) {
|
if (char.class.equals(type)) return Character.valueOf('\0');
|
if (byte.class.equals(type)) return Byte.valueOf((byte) 0);
|
if (short.class.equals(type)) return Short.valueOf((short) 0);
|
if (int.class.equals(type)) return Integer.valueOf(0);
|
if (long.class.equals(type)) return Long.valueOf(0L);
|
if (float.class.equals(type)) return Float.valueOf(0F);
|
if (double.class.equals(type)) return Double.valueOf(0D);
|
return null;
|
}
|
}
|