java腦洞 OpenFeign傳遞Bean參數(shù)解決方案

腦洞的由來

場景:服務(wù)間調(diào)用通過統(tǒng)一的Feign接口實(shí)現(xiàn)多服務(wù)間Api協(xié)同

功能需求

  1. 能通過服務(wù)名調(diào)用,服務(wù)名可動(dòng)態(tài)修改
  2. 能通過url調(diào)用,url地址可動(dòng)態(tài)修改
  3. 由于接口被多服務(wù)繼承,需保證方法體盡量少變動(dòng)
  4. 傳遞參數(shù)有對(duì)應(yīng)的字段和備注

問題

  1. @RequestHeader 注解對(duì)應(yīng)單個(gè)Header配置,header的改變會(huì)導(dǎo)致方法體的變動(dòng)
  2. @RequestParam 注解對(duì)應(yīng)單個(gè)Get請(qǐng)求中param配置,get請(qǐng)求參數(shù)的變動(dòng)會(huì)導(dǎo)致方法體的變動(dòng)
  3. @RequestHeader 支持Map,但是Map對(duì)參數(shù)提示不友好

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請(qǐng)關(guān)注

springboot demo實(shí)戰(zhàn)項(xiàng)目
java 腦洞
java 面試寶典
開源工具

功能實(shí)現(xiàn)

整體項(xiàng)目結(jié)構(gòu)


image.png

可以看到用到的類很少,下面列舉下關(guān)鍵類

RequestParam 通過調(diào)用initMap方法實(shí)現(xiàn)將bean參數(shù)裝入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觸發(fā)調(diào)用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對(duì)RequestHeaderParameterProcessor增強(qiáng),通過Expander觸發(fā)RequestParam的initMap方法,把對(duì)象屬性注入Map中,然后通過headerMapIndex觸發(fā)Map轉(zhuǎn)param

/**
 * RequestHeaderParam注解處理器
 * 通過Expander觸發(fā)RequestParam的initMap方法,把對(duì)象屬性注入Map中,然后通過headerMapIndex觸發(fā)Map轉(zhuǎn)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);
    }
}

實(shí)際運(yùn)用

demo項(xiàng)目地址: 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對(duì)應(yīng)服務(wù)名稱
 * url 對(duì)應(yīng)地址,如果url不為空,則優(yōu)先url,否則走服務(wù)名調(diào)用
 */
@FeignClient(value = "${text.name:TestApiClient}", url = "${text.url:}")
public interface TestApiClient extends TestApi {

}

  1. 實(shí)際使用
@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返回的結(jié)果:" + test);
    }

}

  1. 然后我們來看下測試用的Controller
@RestController
public class TestController {

    @GetMapping("/test")
    public String test(String name, String age, HttpServletRequest request) {
        System.out.println("獲取參數(shù)name:" + name);
        System.out.println("獲取參數(shù)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接收到結(jié)果


    image.png

demo項(xiàng)目導(dǎo)入

參考: http://m.itdecent.cn/p/cd0275a2f5fb

如果這篇文章對(duì)你有幫助請(qǐng)給個(gè)star


image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容