本帖最后由 小蜀哥哥 于 2019-8-8 17:26 编辑
spring cloud 使用 Feign 进行服务调用时,不支持对象参数。 通常解决方法是,要么把对象每一个参数平行展开,并使用 @RequestParam 标识出每一个参数,要么用 @RequestBody 将请求改为 body 传参,虽然这样解决了问题,但是这样限制了传参方式,并且使代码变得很繁重。 以下为完美解决 Feign 对象传参问题的办法 1. 引入如下依赖(可以在maven仓库中搜索 strongfeign) [AppleScript] 纯文本查看 复制代码 <dependency>
<groupId>com.moonciki.strongfeign</groupId>
<artifactId>feign-httpclient</artifactId>
<version>10.2.3</version>
</dependency> 注意:不要使用 10.3.x版本,该版本有问题。如果jar包无法下载请使用 maven 中央仓库。 2. 创建如下三个类 开始时,打算把以下三个类加进仓库中,但由于如下三个类内容不多,并且有很多定制化的可能,因此单独实现。 2.1 ParamModel.java [AppleScript] 纯文本查看 复制代码 package com.moonciki.strongfeign.model.annotation;
import java.lang.annotation.*;
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ParamModel {
String value() default "";
} 2.2 ModelExpander.java [AppleScript] 纯文本查看 复制代码 package com.moonciki.strongfeign.model.expander;
import com.alibaba.fastjson.JSON;
import feign.Param;
import lombok.extern.slf4j.Slf4j;
import java.util.Map;
@Slf4j
public class ModelExpander implements Param.Expander {
public String expand(Object value) {
String objectJson = JSON.toJSONString(value);
return objectJson;
}
@Override
public String expandWithName(Object value, String name) {
String valueExpand = null;
if(value != null){
if(name != null) {
try {
Map<String, Object> jsonMap = (Map<String, Object>)JSON.toJSON(value);
Object getValue = jsonMap.get(name);
if(getValue != null){
valueExpand = getValue.toString();
}
} catch (Exception e) {
log.error("GET VALUE ERROR:", e);
}
}else {
valueExpand = value.toString();
}
}
return valueExpand;
}
} 注:该类需依赖 fastjson,也可根据个人需要修改该方法。 2.3 ParamModelParameterProcessor.java [AppleScript] 纯文本查看 复制代码 package com.moonciki.strongfeign.model.processor;
import com.moonciki.strongfeign.model.annotation.ParamModel;
import com.moonciki.strongfeign.model.expander.ModelExpander;
import feign.MethodMetadata;
import org.springframework.cloud.openfeign.AnnotatedParameterProcessor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
public class ParamModelParameterProcessor implements AnnotatedParameterProcessor {
private static final Class<ParamModel> ANNOTATION = ParamModel.class;
public Class<? extends Annotation> getAnnotationType() {
return ANNOTATION;
}
@Override
public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) {
int parameterIndex = context.getParameterIndex();
Class parameterType = method.getParameterTypes()[parameterIndex];
MethodMetadata data = context.getMethodMetadata();
Field[] fields = parameterType.getDeclaredFields();
for(Field field: fields) {
String name = field.getName();
context.setParameterName(name);
Collection query = context.setTemplateParameter(name, (Collection)data.template().queries().get(name));
data.template().query(name, query);
}
data.indexToExpander().put(context.getParameterIndex(), new ModelExpander());
return true;
}
} 3. 使用注解配置 feign Contract 对象 [AppleScript] 纯文本查看 复制代码 @Bean
public Contract feignContract(){
List<AnnotatedParameterProcessor> processors = new ArrayList<>();
processors.add(new ParamModelParameterProcessor());
processors.add(new PathVariableParameterProcessor());
processors.add(new RequestHeaderParameterProcessor());
processors.add(new RequestParamParameterProcessor());
return new SpringMvcContract(processors);
} 4. 使用方法示例 [AppleScript] 纯文本查看 复制代码 @Primary
@FeignClient(value = "/user", fallback = UserClientFallback.class)
public interface UserClient {
/**
* demo post
* @return
*/
@PostMapping("/demoPost")
Result demoPost(@ParamModel UserAccount userAccount);
/**
* demo get
* @return
*/
@GetMapping("/demoGet")
Result demoPost(@ParamModel UserAccount userAccount);
} 使用时,只需要在对象前加 @ParamModel 注解即可 需要同时传递对象及基本类型参数时, @ParamModel 可以与 @RequestParam("jobName") 同时使用在不同参数上。
|