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 降级处理的安全替代实现。
*
*
依赖版本假定所有泛型返回类型均为 {@link Class}。对于
* {@code Map} 等声明并不成立,其运行时类型为
* {@link ParameterizedType};此前转换失败会以 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);
// 仅显式声明返回 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