簡體   English   中英

我們可以傳遞方法參數的值和注解嗎

[英]Can we pass value and annotation of method parameters

為了管理 swagger 文檔,我對調用 API 的方法使用了自定義注釋

@SwagRef(method = POST, url = "/my/api/{pathParam1}")
  public Response callMyAPI(
      @MyParam(name = "pathParam1", required = true, in = PATH) String p1,
      @MyParam(name = "param2", required = false, in = QUERY) String p2) {
    return given()
            .pathParam("pathParam1", p1)
            .queryParam("param2", p2)
            .get();
  }

有一段單獨的代碼可以驗證 Swagger/api/docs 與注釋。 但是我想知道是否有可能以某種方式使用注釋中所有這些已經提供的數據,並有一個通用代碼,我可以在其中傳遞方法引用或參數引用,並且可以使用注釋構建 RequestSpecification。

我嘗試了反射,但我無法使用反射從方法獲取參數的值

我只能推導出方法類型和 API,因為它是使用 methodName 和 stackTrace 的常量

    private SwagRef defineSwaggerInfo() {
        List<StackTraceElement> stackTrace = asList(currentThread().getStackTrace());
        return stackTrace.stream()
            .map(tryOrNull(element -> Pair.with(element.getMethodName(), forName(element.getClassName()))))
            .filter(Objects::nonNull)
            .filter(pair -> MyAPI.class.isAssignableFrom(pair.getValue1()))
            .map(pair -> with(pair.getValue0(), asList(pair.getValue1().getDeclaredMethods())))
            .flatMap(
                tryOrNull(
                    pair ->
                        pair.getValue1().stream()
                            .filter(method -> Objects.equals(method.getName(), pair.getValue0()))
                            .peek(method -> method.setAccessible(true))
                            .map(method -> method.getAnnotation(SwagRef.class))))
            .filter(Objects::nonNull)
            .findFirst()
            .orElseThrow();
}

但是我無法提出使用方法參數構建請求規范的通用函數

我嘗試查看 AspectJ,但無法正確嵌入

無法通過反射從堆棧中獲取實際參數值。 事實上,甚至無法保證正在進行的調用的參數值此時仍在堆棧中。

最接近執行自動參數處理的方法是在接口中聲明方法並生成代理

interface FrontEnd {
    public static FrontEnd get() {
        return (FrontEnd)Proxy.newProxyInstance(FrontEnd.class.getClassLoader(),
            new Class<?>[]{FrontEnd.class}, (proxy, method, args) -> {
                if(method.getDeclaringClass() == Object.class) {
                    switch(method.getName()) {
                        case "toString": return
                            FrontEnd.class.getName()+'@'+System.identityHashCode(proxy);
                        case "equals": return proxy == args[0];
                        case "hashCode": return System.identityHashCode(proxy);
                        default: throw new AssertionError();
                    }
                }
                SwagRef swagRef = method.getAnnotation(SwagRef.class);
                if(swagRef == null) throw new IncompatibleClassChangeError();
                MyParam[] p = Arrays.stream(method.getParameterAnnotations())
                    .map(pa -> Arrays.stream(pa)
                        .filter(a -> a.annotationType() == MyParam.class)
                        .findFirst().orElseThrow(
                            () -> new IllegalStateException("missing required @MyParam")))
                    .toArray(MyParam[]::new);
                Map<String,String> map = IntStream.range(0, args.length).boxed()
                    .filter(i -> p[i].required() || args[i] != null)
                    .collect(Collectors.toMap(i -> p[i].name(), i -> args[i].toString()));
                // do actual invocation logic here
                System.out.println(
                    "operation: "+swagRef.method()+' '+swagRef.url()+", "+map);
                return null;
        });
    }

    @SwagRef(method = POST, url = "/my/api/{pathParam1}")
    public Response callMyAPI(
        @MyParam(name = "pathParam1", required = true, in = PATH) String p1,
        @MyParam(name = "param2", required = false, in = QUERY) String p2);
}

您可以向該接口添加更多方法,以相同的方式處理,假設它們都具有必要的注釋。

從 Java 9 開始,您可以在interface使用private方法,我更喜歡這里。

interface FrontEnd {
    public static FrontEnd get() {
        return (FrontEnd)Proxy.newProxyInstance(FrontEnd.class.getClassLoader(),
            new Class<?>[]{FrontEnd.class}, FrontEnd::callImpl);
    }
    @SwagRef(method = POST, url = "/my/api/{pathParam1}")
    public Response callMyAPI(
        @MyParam(name = "pathParam1", required = true, in = PATH) String p1,
        @MyParam(name = "param2", required = false, in = QUERY) String p2);

    private static Object callImpl(Object proxy, Method method, Object[] args) {
        if(method.getDeclaringClass() == Object.class) {
            switch(method.getName()) {
                case "toString": return
                    FrontEnd.class.getName()+'@'+System.identityHashCode(proxy);
                case "equals": return proxy == args[0];
                case "hashCode": return System.identityHashCode(proxy);
                default: throw new AssertionError();
            }
        }
        SwagRef swagRef = method.getAnnotation(SwagRef.class);
        if(swagRef == null) throw new IncompatibleClassChangeError();
        MyParam[] p = Arrays.stream(method.getParameterAnnotations())
            .map(pa -> Arrays.stream(pa)
                .filter(a -> a.annotationType() == MyParam.class)
                .findFirst().orElseThrow(
                    () -> new IllegalStateException("missing required @MyParam")))
            .toArray(MyParam[]::new);
        Map<String,String> map = IntStream.range(0, args.length).boxed()
            .filter(i -> p[i].required() || args[i] != null)
            .collect(Collectors.toMap(i -> p[i].name(), i -> args[i].toString()));
        // do actual invocation logic here
        System.out.println("operation: "+swagRef.method()+' '+swagRef.url()+", "+map);
        return null;
    }
}

或者,您可以拆分接口和一個可能是非公共的輔助類之間的邏輯。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM