spring cloud 使用 Feign 进行服务调用时, 不支持对象参数.
通常解决方法是, 要么把对象每一个参数平行展开, 并使用 @RequestParam 标识出每一个参数, 要么用 @RequestBody 将请求改为 body 传参, 虽然这样解决了问题, 但是这样限制了传参方式, 并且使代码变得很繁重.
以下为完美解决 Feign 对象传参问题的办法.
1. 引入如下依赖 (可以在 maven 仓库中搜索 strongfeign)
- <dependency>
- <groupId>com.moonciki.strongfeign</groupId>
- <artifactId>feign-httpclient</artifactId>
- <version>10.2.3</version>
- </dependency>
该源码修改自 https://github.com/OpenFeign/feign , 提交过 pr, 但是项目原作者并没有采纳, pr 地址如下: https://github.com/OpenFeign/feign/pull/949
之后为了同步到了 maven 仓库, 做了相应删减及 pom 的变更, 具体改动可参考 GitHub. 地址: https://github.com/cdmamata/strong-feign
注意: 不要使用 10.3.x 版本, 该版本有问题. 如果 jar 包无法下载请使用 maven 中央仓库.
2. 创建如下三个类
开始时, 打算把以下三个类加进仓库中, 但由于如下三个类内容不多, 并且有很多定制化的可能, 因此单独实现.
2.1 ParamModel.java
- 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
- 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
- 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 对象
- @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. 使用方法示例
- @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") 同时使用在不同参数上.
来源: https://www.cnblogs.com/moonciki/p/11320548.html