简体   繁体   English

我们可以传递方法参数的值和注解吗

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

To manage swagger documentations I am using custom annotations for the methods which call the API为了管理 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();
  }

There is a separate piece of code which validates the Swagger/api/docs vs the annotations.有一段单独的代码可以验证 Swagger/api/docs 与注释。 However I'm wondering is it possible to somehow use all this already presented data in the annotations and have a common code where I can pass the method reference or the parameter reference and the RequestSpecification can be built using the annotations.但是我想知道是否有可能以某种方式使用注释中所有这些已经提供的数据,并有一个通用代码,我可以在其中传递方法引用或参数引用,并且可以使用注释构建 RequestSpecification。

I tried with reflection, but I'm unable to fetch the value of parameters using reflection from method我尝试了反射,但我无法使用反射从方法获取参数的值

I was only able to deduce the method type and API since it's constant using the methodName and stackTrace我只能推导出方法类型和 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();
}

But I'm not able to come up with a generic function for Building the request spec using method parameters但是我无法提出使用方法参数构建请求规范的通用函数

I tried looking at AspectJ but wasn't able to embed it properly我尝试查看 AspectJ,但无法正确嵌入

There is no way to get the actual parameter values from the stack via Reflection.无法通过反射从堆栈中获取实际参数值。 In fact, there's not even a guaranty that the parameter values of an ongoing invocation are still on the stack at that point.事实上,甚至无法保证正在进行的调用的参数值此时仍在堆栈中。

The closest you can get to perform automated parameter processing, is to declare the methods in an interface and generate a proxy :最接近执行自动参数处理的方法是在接口中声明方法并生成代理

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);
}

You may add more methods to that interface, to be handled the same way, assuming that they all have the necessary annotations.您可以向该接口添加更多方法,以相同的方式处理,假设它们都具有必要的注释。

Starting with Java 9, you can use a private method in the interface , which I would prefer here.从 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;
    }
}

Alternatively, you may split up the logic between the interface and a, possibly non-public, helper class.或者,您可以拆分接口和一个可能是非公共的辅助类之间的逻辑。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM