java脑洞 OpenFeign传递Bean参数解决方案

脑洞的由来

场景:服务间调用通过统一的Feign接口实现多服务间Api协同

功能需求

  1. 能通过服务名调用,服务名可动态修改
  2. 能通过url调用,url地址可动态修改
  3. 由于接口被多服务继承,需保证方法体尽量少变动
  4. 传递参数有对应的字段和备注

问题

  1. @RequestHeader 注解对应单个Header配置,header的改变会导致方法体的变动
  2. @RequestParam 注解对应单个Get请求中param配置,get请求参数的变动会导致方法体的变动
  3. @RequestHeader 支持Map,但是Map对参数提示不友好

Git地址

https://gitee.com/wqrzsy/lp-demo/tree/master/lp-open-api

MAVEN

        <dependency>
            <groupId>io.github.wqr503</groupId>
            <artifactId>lp-open-api</artifactId>
            <version>1.0.0</version>
        </dependency>

更多demo请关注

springboot demo实战项目
java 脑洞
java 面试宝典
开源工具

功能实现

整体项目结构
image.png

可以看到用到的类很少,下面列举下关键类

RequestParam 通过调用initMap方法实现将bean参数装入Map

public interface RequestParam extends Map<String, Object> {

    default void initMap() {
        initMap(true, Util.UTF_8);
    }

    default void initMap(boolean encodeValue) {
        initMap(encodeValue, Util.UTF_8);
    }

    void initMap(boolean encodeValue, Charset charset);

}

ParameterExpander 通过Expander触发调用RequestParam的initMap方法

/**
 * param 处理器
 */
public class ParameterExpander implements Param.Expander {

    private boolean encode = false;

    private Charset charset = Util.UTF_8;

    public static ParameterExpander build(boolean encode, Charset charset) {
        ParameterExpander expander = new ParameterExpander();
        expander.encode = encode;
        expander.charset = charset;
        return expander;
    }

    @Override
    public String expand(Object value) {
        if(BlankAide.isBlank(value)) {
            return null;
        }
        if(value instanceof RequestParam) {
            RequestParam requestParam = (RequestParam) value;
            requestParam.initMap(encode, charset);
            return "requestParam.initMap()";
        }
        return value.toString();
    }

}

RequestHeaderParamParameterProcessor对RequestHeaderParameterProcessor增强,通过Expander触发RequestParam的initMap方法,把对象属性注入Map中,然后通过headerMapIndex触发Map转param

/**
 * RequestHeaderParam注解处理器
 * 通过Expander触发RequestParam的initMap方法,把对象属性注入Map中,然后通过headerMapIndex触发Map转param
 */
public class RequestHeaderParamParameterProcessor extends RequestHeaderParameterProcessor {

    private boolean encode = false;

    private Charset charset = Util.UTF_8;

    public static RequestHeaderParamParameterProcessor build(boolean encode, Charset charset) {
        RequestHeaderParamParameterProcessor requestHeaderParamParameterProcessor = new RequestHeaderParamParameterProcessor();
        requestHeaderParamParameterProcessor.encode = encode;
        requestHeaderParamParameterProcessor.charset = charset;
        return requestHeaderParamParameterProcessor;
    }

    @Override
    public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) {
        int parameterIndex = context.getParameterIndex();
        Class<?> parameterType = method.getParameterTypes()[parameterIndex];
        MethodMetadata data = context.getMethodMetadata();
        if (RequestParam.class.isAssignableFrom(parameterType)) {
            context.setParameterName("s");
            Map<Integer, Param.Expander> indexToExpander= data.indexToExpander();
            indexToExpander.put(parameterIndex, ParameterExpander.build(encode, charset));
            data.indexToEncoded().put(parameterIndex, Boolean.FALSE);
            MethodMetadata metadata = context.getMethodMetadata();
            if (metadata.headerMapIndex() == null) {
                metadata.headerMapIndex(parameterIndex);
            }
            return true;
        }
        return super.processArgument(context, annotation, method);
    }
}

实际运用

demo项目地址: https://gitee.com/wqrzsy/lp-demo/tree/master/lp-open-api-demo

  1. 首先开启功能的注解 @EnableOpenApiClients
@SpringBootApplication
@EnableOpenApiClients
public class TestApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(TestApplication.class);
    }

}

  1. api定义
public interface TestApi {

    @GetMapping(value = "/test", produces = {MediaType.APPLICATION_JSON_UTF8_VALUE})
    String test(@RequestParam GetRequestParam param, @RequestHeader HeaderParam header);

}
  1. FeignClient
/**
 * value对应服务名称
 * url 对应地址,如果url不为空,则优先url,否则走服务名调用
 */
@FeignClient(value = "${text.name:TestApiClient}", url = "${text.url:}")
public interface TestApiClient extends TestApi {

}

  1. 实际使用
@Service
public class TestService {

    @Autowired
    private TestApiClient testApiClient;

    @PostConstruct
    public void init() {
        GetRequestParam getRequestParam = new GetRequestParam();
        getRequestParam.setName("我是HelloWorld");
        getRequestParam.setAge(10);
        HeaderParam headerParam = new HeaderParam();
        headerParam.setHeader_key_2("我是key2");
        headerParam.setHeader_key_1("我是Key1");
        String test = testApiClient.test(getRequestParam, headerParam);
        System.out.println("testApiClient返回的结果:" + test);
    }

}

  1. 然后我们来看下测试用的Controller
@RestController
public class TestController {

    @GetMapping("/test")
    public String test(String name, String age, HttpServletRequest request) {
        System.out.println("获取参数name:" + name);
        System.out.println("获取参数age:" +age);
        System.out.println("获取header的header_key_1:" + request.getHeader("header_key_1"));
        System.out.println("获取header的header_key_2:" + request.getHeader("header_key_2"));
        return "success";
    }

}
  1. Controller接收到结果
    image.png

demo项目导入

参考: https://www.jianshu.com/p/cd0275a2f5fb

如果这篇文章对你有帮助请给个star
image.png

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值