Feign get请求通过对象传递url参数,且支持对象中的属性排序

1.get请求参数比较多时候写param是很累的一种方式:

@GetMapping(value = "/fapi/v1/positionSide/dual")
PositionSideResponse getPositionSide(@RequestHeader(value = "X-MBX-APIKEY") String apikey, @SpringQueryMap BaseRequest request);

使用 @SpringQueryMap 可以自动将对象转换为get参数拼接在url上

2.当实体对象中的熟悉需要排序时怎么办呢? 比如sign的参数必须放参数末尾这种情况?

这个时候需要我们自定义实现 BeanQueryMapEncoder 代码如下

import feign.Param;
import feign.QueryMapEncoder;
import feign.codec.EncodeException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;

import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
public class CustomerBeanQueryMapEncoder implements QueryMapEncoder {

    private final Map<Class<?>, CustomerBeanQueryMapEncoder.ObjectParamMetadata> classToMetadata =
            new HashMap<>();

    @Override
    public Map<String, Object> encode(Object object) throws EncodeException {
        try {
            CustomerBeanQueryMapEncoder.ObjectParamMetadata metadata = getMetadata(object.getClass());
            Map<String, Object> propertyNameToValue = new LinkedHashMap<>();
            for (PropertyDescriptor pd : metadata.objectProperties) {
                Method method = pd.getReadMethod();
                Object value = method.invoke(object);
                if (value != null && value != object) {
                    Param alias = method.getAnnotation(Param.class);
                    String name = alias != null ? alias.value() : pd.getName();
                    propertyNameToValue.put(name, value);
                }
            }
            return propertyNameToValue;
        } catch (IllegalAccessException | IntrospectionException | InvocationTargetException e) {
            throw new EncodeException("Failure encoding object into query map", e);
        }
    }

    private CustomerBeanQueryMapEncoder.ObjectParamMetadata getMetadata(Class<?> objectType) throws IntrospectionException {
        CustomerBeanQueryMapEncoder.ObjectParamMetadata metadata = classToMetadata.get(objectType);
        if (metadata == null) {
            metadata = CustomerBeanQueryMapEncoder.ObjectParamMetadata.parseObjectType(objectType);
            classToMetadata.put(objectType, metadata);
        }
        return metadata;
    }

    private static class ObjectParamMetadata {

        private final List<PropertyDescriptor> objectProperties;

        private ObjectParamMetadata(List<PropertyDescriptor> objectProperties) {
            this.objectProperties = Collections.unmodifiableList(objectProperties);
        }

        private static CustomerBeanQueryMapEncoder.ObjectParamMetadata parseObjectType(Class<?> type)
                throws IntrospectionException {
            List<PropertyDescriptor> properties = new ArrayList<PropertyDescriptor>();

            for (PropertyDescriptor pd : Introspector.getBeanInfo(type).getPropertyDescriptors()) {
                boolean isGetterMethod = pd.getReadMethod() != null && !"class".equals(pd.getName());
                if (isGetterMethod) {
                    properties.add(pd);
                }
            }

            /**
             * 实现排序
             */
            properties = properties.stream().sorted(Comparator.comparing(x -> {
                int orderValue = -1;
                try {
                    Field field = type.getDeclaredField(x.getName());
                    Order orderAnno = field.getAnnotation(Order.class);
                    if (orderAnno != null) {
                        orderValue = orderAnno.value();
                    }
                } catch (NoSuchFieldException e) {
                    log.error("NoSuchFieldException", e);
                }
                return orderValue;
            })).collect(Collectors.toList());

            return new CustomerBeanQueryMapEncoder.ObjectParamMetadata(properties);
        }
    }
}

然后对feign的queryMapEncoder赋值成我们的

import feign.Feign;
import feign.Retryer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FeignConfiguration {

    /**
     * @Description 替换解析queryMap的类为自定义map类
     */
    @Bean
    public Feign.Builder feignBuilder() {
        return Feign.builder()
                .queryMapEncoder(new CustomerBeanQueryMapEncoder())
                .retryer(Retryer.NEVER_RETRY);
    }

}

此时在实体对象的属性上标注Order即可实现Encode后字符串的排序

    private Long recvWindow;

    /**
     * 下单时间戳
     */
    private Long timestamp = System.currentTimeMillis();

    /**
     * 签名值
     */
    @Order(value = Integer.MAX_VALUE)
    private String signature;

上一篇:[spring cloud] feign声明


下一篇:feign list 的 post 请求坑记录