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;
|
|
/**
|
* Safe replacement for the common Sentinel Feign fallback.
|
*
|
* <p>The dependency version assumes every generic return type is a Class. That
|
* is false for declarations such as {@code Map<String, Object>}, whose runtime
|
* type is {@link ParameterizedType}; the failed cast used to hide the original
|
* downstream exception behind an 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);
|
|
// Only methods that explicitly declare ResponseData may receive a
|
// ResponseData fallback. Returning it from Map/List methods causes a
|
// second ClassCastException in the Feign proxy.
|
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;
|
}
|
}
|