贴一个我自己用的方案吧,这个的缺点是 Response 如果是非成功状态抛出异常是会被 feign 捕获包装重新抛出,里面包含了 decode 的信息。
public class AutoBoxingDecoder implements Decoder {
    private final SpringDecoder delegate;
    public AutoBoxingDecoder(SpringDecoder decoder) {
        this.delegate = decoder;
    }
    @
Override    public Object decode(Response response, Type type) throws IOException, FeignException {
        if (type instanceof NormalResponse) {
            return delegate.decode(response,type);
        } else if (type instanceof ParameterizedType) {
            ParameterizedType t = (ParameterizedType) type;
            if (t.getRawType() instanceof NormalResponse) {
                return delegate.decode(response,type);
            }
        }
        ParameterizedType pt = new MyParameterizedType(type);
        Object object = delegate.decode(response,pt);
        if (object instanceof NormalResponse) {
            return unpackOrThrow((NormalResponse<?>) object);
        }
        return object;
    }
    private Object unpackOrThrow(NormalResponse<?> res) {
        if (res == null) {
            throw new AppException("client response body is empty.");
        }
        if (!res.isSuccess()) {
            throw new AppException(res.getMessage());
        }
        return res.getData();
    }
    static class MyParameterizedType implements ParameterizedType {
        private final Type argument;
        MyParameterizedType(Type argument) {
            this.argument = argument;
        }
        @
Override        public Type[] getActualTypeArguments() {
            return new Type[]{argument};
        }
        @
Override        public Type getRawType() {
            return NormalResponse.class;
        }
        @
Override        public Type getOwnerType() {
            return null;
        }
    }
}