java - 我们可以传递方法参数的值和注释吗
问题描述
为了管理 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 开始,您可以使用private
中的方法interface
,我更喜欢这里。
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;
}
}
或者,您可以在接口和可能是非公共的辅助类之间拆分逻辑。
推荐阅读
- python - 'KeyedVectors' 对象没有属性 'wv' / 词汇属性已从 Gensim 4.0.0 中的 KeyedVector 中删除
- kubernetes - GCP GKE - 具有 2 个暴露端口的同一应用程序的原生 GKE 入口的健康检查
- c# - c#如何调用另一个类的列表
- ssis - [OData Source [516]] 错误:OData 源无法处理数据。来自 HRESULT 的异常:0xC0047072 SSIS 错误 DTS_E_PRIMEOUTPUTFAILED
- c# - 重定向
在 web.config 中不重定向 - c# - addScriptToEvaluateOnNewDocument 被重置
- firebase - Vue.js:通过循环遍历数组值来绑定文本字段的值
- terraform - 在 Terraform 云中删除工作区的 Sentinel 策略
- docker - 更新的 API 文档在通过 docker 运行时未加载到 swagger-ui 中
- javascript - 如何通过javascript设置flaskform字段值