如何解决尝试通过反射'getAnnotatedParameterTypes'访问时,参数注释为null
我正在用AspectJ编织一种方法,并对其应用了Around
建议。然后,在建议逻辑中,我想访问该方法的所有带注释的参数。我这样做是为了过滤我要查找的特定注释。
问题在于,在我调用getAnnotatedParameterTypes()
的{{1}}之后,我收到了java.lang.reflect
的数组。我可以在那里找到期望的参数。但是,当我要访问该参数的注释类型时-因为我想按其类型进行过滤-没有注释。
我希望它会出现-很好,因为它说它是AnnotatedType
-那么注释:D
这是要浏览的代码
AnnotatedType
日志输出
2020-10-10 22:17:11.821信息215068 --- [测试人员] com.mystuff.Aspect:[]
我的注释
@Around("@annotation(com.mystuff.client.annotation.Query)")
public void doStuff(ProceedingJoinPoint joinPoint) {
Method[] methods = joinPoint.getSignature().getDeclaringType().getmethods();
Optional<Method> first = Arrays.stream(methods).findFirst();
if (first.isPresent()) {
Method method = first.get();
AnnotatedType[] annotatedParameterTypes = method.getAnnotatedParameterTypes();
AnnotatedType annotatedParameterType = annotatedParameterTypes[0];
LOG.info(Arrays.toString(annotatedParameterType.getAnnotations()));
}
}
测试整个魔术的课程
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Query{
}
@Target(ElementType.ParaMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface Response {
}
解决方法
您的方面代码有几个问题:
-
您的目标方法返回了一些内容,但是advice方法的返回类型为
void
,也就是说,它隐式绝不会匹配void
方法以外的任何内容。但是,它绝对不会与您的示例useData(..)
方法匹配。因此,如果要限制返回类型,则需要将返回类型设为Object
或TestResponseModel
。 -
@Around
建议永远不会调用joinPoint.proceed()
,即目标方法将不会执行而是被跳过。 -
如果您只想记录
@Response
参数,而又不想在继续之前/之后修改任何参数或结果,那么实际上一个简单的@Before
建议就足够了。不过,如果您想对这些参数做一些特殊的事情,我将在示例代码中保留您的周围建议。 -
建议方法的前两行执行以下操作:
- 获取目标类中所有方法的数组。
- 找到第一种方法。
这没有多大意义。为什么您总是不考虑第一种方法而总是用第一种方法做某事?您想在建议所拦截的目标方法上标识参数注释,不是吗?第一个方法的第一个参数可能没有任何注释,这就是为什么没有任何注释的原因。您实际上很幸运,第一个方法根本没有参数,否则
annotatedParameterTypes[0]
会产生“数组索引超出范围”异常。
这是您要执行的操作。顺便说一句,我在这里提供完整的MCVE,就像您应该首先做的那样。我使用的是普通AspectJ,而不是Spring AOP,所以我不使用任何@Component
注释。但是,如果您是Spring用户,则可以同时使方面和目标类Spring组件/ bean都可以正常工作。
注释+虚拟助手类:
package de.scrum_master.app;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Retention(RUNTIME)
@Target(METHOD)
public @interface Query {}
package de.scrum_master.app;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Retention(RUNTIME)
@Target(PARAMETER)
public @interface Response {}
package de.scrum_master.app;
public class TestResponseModel {}
带有正/负测试用例和驱动程序应用程序的目标类
package de.scrum_master.app;
class TestCandidate {
@Query
public TestResponseModel useData(@Response TestResponseModel model) {
return model;
}
@Query
public TestResponseModel dummyOne(TestResponseModel model) {
return model;
}
public TestResponseModel dummyTwo(@Response TestResponseModel model) {
return model;
}
@Query
public TestResponseModel multipleResponses(@Response TestResponseModel model,@Response String anotherResponse,int i) {
return model;
}
public static void main(String[] args) {
TestCandidate candidate = new TestCandidate();
TestResponseModel model = new TestResponseModel();
candidate.dummyOne(model);
candidate.dummyTwo(model);
candidate.useData(model);
candidate.multipleResponses(model,"foo",11);
}
}
期望将为方法useData
和multipleResponses
触发建议,并且后一种方法中多个@Response
参数的特殊情况也可以由方面正确处理。
@Around
方面的变体:
package de.scrum_master.aspect;
import java.lang.annotation.Annotation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import de.scrum_master.app.Response;
@Aspect
public class QueryResponseInterceptor {
@Around(
"@annotation(de.scrum_master.app.Query) && " +
"execution(* *(..,@de.scrum_master.app.Response (*),..))"
)
public Object doStuff(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println(joinPoint);
Object[] args = joinPoint.getArgs();
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Annotation[][] annotationMatrix = methodSignature.getMethod().getParameterAnnotations();
for (int i = 0; i < args.length; i++) {
for (Annotation annotation : annotationMatrix[i]) {
if (annotation.annotationType().equals(Response.class)) {
System.out.println(" " + args[i]);
break;
}
}
}
return joinPoint.proceed();
}
}
请注意,execution()
切入点如何限制带有参数的方法带有@Response
注释的参数,无论它们在参数列表中的何处出现。
@Before
方面的变体:
如果您只想记录带注释的参数,则可以使用以下更简单的变体:@Before
建议和更少的样板:
package de.scrum_master.aspect;
import java.lang.annotation.Annotation;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import de.scrum_master.app.Response;
@Aspect
public class QueryResponseInterceptor {
@Before(
"@annotation(de.scrum_master.app.Query) && " +
"execution(* *(..,..))"
)
public void doStuff(JoinPoint joinPoint) {
System.out.println(joinPoint);
Object[] args = joinPoint.getArgs();
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Annotation[][] annotationMatrix = methodSignature.getMethod().getParameterAnnotations();
for (int i = 0; i < args.length; i++) {
for (Annotation annotation : annotationMatrix[i]) {
if (annotation.annotationType().equals(Response.class)) {
System.out.println(" " + args[i]);
break;
}
}
}
}
}
看到了吗?现在,您确实可以使用void
返回类型,无需调用proceed()
,因此也不必抛出Throwable
。
控制台日志:
对于两个方面的变体,控制台日志都是相同的。
execution(TestResponseModel de.scrum_master.app.TestCandidate.useData(TestResponseModel))
de.scrum_master.app.TestResponseModel@71318ec4
execution(TestResponseModel de.scrum_master.app.TestCandidate.multipleResponses(TestResponseModel,String,int))
de.scrum_master.app.TestResponseModel@71318ec4
foo
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。