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. * *

The dependency version assumes every generic return type is a Class. That * is false for declarations such as {@code Map}, whose runtime * type is {@link ParameterizedType}; the failed cast used to hide the original * downstream exception behind an AssertionError.

*/ public class DefaultFeignFallback implements MethodInterceptor { private static final Logger log = LoggerFactory.getLogger(DefaultFeignFallback.class); private final Class targetType; private final String targetName; private final Throwable cause; public DefaultFeignFallback(Class 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(); if (Set.class.isAssignableFrom(type)) return new HashSet(); if (Map.class.isAssignableFrom(type)) return new HashMap(); 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; } }