| New file |
| | |
| | | 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; |
| | | |
| | | /** |
| | | * Account-side replacement for the common Sentinel Feign fallback. |
| | | * |
| | | * <p>The dependency implementation casts {@link Method#getGenericReturnType()} |
| | | * directly to {@link Class}. That fails for generic Feign methods such as |
| | | * {@code List<IntegralCommodity>}; resolve the raw type first so a proper |
| | | * empty fallback value can be returned.</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); |
| | | if (ResponseData.class.equals(returnType)) { |
| | | return ResponseData.builder() |
| | | .code(ErrorCodeConstants.FAIL.getValue()) |
| | | .msg("系统降级,请稍后再试!") |
| | | .data(defaultValue(rawType(firstTypeArgument(declaredReturnType)))) |
| | | .build(); |
| | | } |
| | | return defaultValue(returnType); |
| | | } |
| | | |
| | | 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; |
| | | } |
| | | } |