JiuyeXD's Blog
九叶
九叶博主

越努力 越幸运

登录
夜间

[笔记] OpenFeign中请求实体参数以及设置实体类字段别名

1. 问题

今天使用OpenFeign调用外部接口的时候发现如果接口不是Json格式通讯的话且请求参数较多的情况下 只能使用@RequestParam将每个参数写入,这样工作量会增加不少并且代码可读性以及后期维护成本较高很麻烦。

2. 解决方案

通过翻阅资料以及OpenFeign源码发现有一个注解为@SpringQueryMap可以将实体类作为入参,

但是如果对方接口接受的参数为下划线命名规则而非驼峰规则这样子和自己这边的代码风格不统一

这样子真的很头疼 作为强迫症我决定翻阅源码从底层入手。

其实OpenFeign的开发者早就想到这种情况的发生 有一个自定义注解解析类QueryMapParameterProcessor

public class QueryMapParameterProcessor implements AnnotatedParameterProcessor {
    private static final Class<SpringQueryMap> ANNOTATION = SpringQueryMap.class;

    public QueryMapParameterProcessor() {
    }

    public Class<? extends Annotation> getAnnotationType() {
        return ANNOTATION;
    }

    public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) {
        int paramIndex = context.getParameterIndex();
        MethodMetadata metadata = context.getMethodMetadata();
        if (metadata.queryMapIndex() == null) {
            metadata.queryMapIndex(paramIndex);
            metadata.queryMapEncoded(((SpringQueryMap)SpringQueryMap.class.cast(annotation)).encoded());
        }

        return true;
    }
}

此段代码对@SpringQueryMap做了增强处理

继续深入挖掘 发现在 BeanQueryMapEncoder 类中的encode方法对 @Param 注解进行拦截解析

    public Map<String, Object> encode(Object object) throws EncodeException {
        try {
            BeanQueryMapEncoder.ObjectParamMetadata metadata = this.getMetadata(object.getClass());
            Map<String, Object> propertyNameToValue = new HashMap();
            Iterator var4 = metadata.objectProperties.iterator();

            while(var4.hasNext()) {
                PropertyDescriptor pd = (PropertyDescriptor)var4.next();
                Method method = pd.getReadMethod();
                Object value = method.invoke(object);
                if (value != null && value != object) {
                    Param alias = (Param)method.getAnnotation(Param.class);
                    String name = alias != null ? alias.value() : pd.getName();
                    propertyNameToValue.put(name, value);
                }
            }

            return propertyNameToValue;
        } catch (IntrospectionException | InvocationTargetException | IllegalAccessException var10) {
            throw new EncodeException("Failure encoding object into query map", var10);
        }
    }

其中 pd.getReadMethod() 方法是获得实体类参数中get方法的信息

所以我们只要将 @Param 方法定义在需要起别名的参数的get方法上就可以 如下所示

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class LeigodLoginDto implements Serializable {

	private static final long serialVersionUID = 8438771923132322423L;

	private String username;
	private String password;
	private String userType;
	private String srcChannel;
	private String code;
	private String countryCode;
	private String lang;

	@Param("user_type")
	public String getUserType() {
		return userType;
	}

	@Param("src_channel")
	public String getSrcChannel() {
		return srcChannel;
	}

	@Param("country_code")
	public String getCountryCode() {
		return countryCode;
	}
}

通过DEBUG发现以上修改生效

问题解决接口成功返回参数

{"msg":"成功","code":0,"data":{"login_info":{"account_token":"7l1nwcwMlqB7qWwV9hDgjQ***********DeG6WBtkGRx8ZPFkWM0","expiry_time":"2023-05-06 06:29:57","nn_token":"86795b20e9854*****3efc5d0565d53"},"user_info":{"nickname":"138****9728","email":"","mobile":"138****9728","avatar":"https://picture.leigod.com/uploads/leigod/2020-01-27/*************.jpeg","region_code":1}}}
THE END