当前位置: 首页 > 知识库问答 >
问题:

试图通过反射“getAnnotatedParameterTypes()”访问参数注释时,该注释为null

丰赞
2023-03-14

我用AspectJ编写了一个方法,并对其应用了一个建议。在advice逻辑中,我希望访问该方法的所有注释参数。我这样做是为了过滤我正在寻找的特定注释。

问题是,在我调用getAnNotatedParameterTypes()java.lang.reflect后,我收到了一个AnNotatedType数组。我可以在那里找到我正在寻找的预期参数。但是,当我想访问该参数的注释类型时——因为我想按其类型过滤——没有注释存在。

我希望它会出现-因为它说它是一个带注释的类型-那么注释在哪里:D

这是要查看的代码

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

日志输出

2020-10-10 22:17:11.821信息215068---[测试工作者]com。迈斯塔夫。方面:[]

我的批注

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Query{

}


@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface Response {

}

整个魔法测试的班级

@Component
class TestCandidate {
    @Query
    public TestResponseModel useData(@Response TestResponseModel model){
        return model;
    }
}

共有1个答案

沈实
2023-03-14

您的方面代码有几个问题:

>

  • 您的目标方法返回一些东西,但通知方法的返回类型是val,即它永远不会隐式匹配除val方法之外的任何东西。但是,它肯定不会匹配您的示例useData(...)方法。因此,如果您想限制返回类型,您需要创建返回类型ObjectTestResseModel

    周围的通知从不调用连接点。继续(),即不会执行目标方法,而是跳过该方法。

    如果您只想记录响应参数,而不想在继续之前/之后修改任何参数或结果,实际上一个简单的建议就足够了。不过,我将在我的示例代码中保留您的建议,以防您想对这些参数做一些特殊的处理。

    建议方法中的前两行执行以下操作:

    1. 获取目标类中所有方法的数组
    2. 找到第一种方法

    这没有多大意义。为什么你总是用第一种方法而不管它是什么方法?您想识别被通知截获的目标方法上的参数注释,不是吗?可能第一个方法的第一个参数没有任何注释,这就是为什么没有记录任何注释。实际上,幸运的是,第一个方法有一个参数,否则,annotatedParameterTypes[0]将产生“数组索引越界”异常。

    这是你想要做的。顺便说一句,我在这里介绍一个完整的MCVE,因为你应该在第一个地方做。我使用的是纯AspectJ,而不是Spring AOP,因此我不使用任何组件注释。但是,如果您是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);
      }
    }
    

    期望为方法useDatamultiple响应触发建议,并且后一种方法中的多个@响应参数的特殊情况也由方面正确处理。

    @周围方面变体:

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

    请注意执行()切入点如何限制带有@响应注释的参数的方法,无论它们可能出现在参数列表中的任何位置。

    方面变量之前:

    如果您只想记录带注释的参数,一个更简单的变体是在这方面,在建议之前加上一个@和更少的样板文件:

    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(* *(.., @de.scrum_master.app.Response (*), ..))"
      ) 
      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返回类型,不需要调用procedue(),因此也不需要抛出抛出。

    控制台日志:

    对于这两个方面变体,控制台日志是相同的。

    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
    

  •  类似资料:
    • 问题内容: 这是一个测试类: 这是我的输出: 我缺少通过反射使注释可见的什么? 我是否仅需要检查它们的存在就需要注释处理器? 问题答案: 为了在运行时访问注释,它需要具有运行时的保留策略。 否则,注释将被丢弃,并且JVM无法识别它们。 有关更多信息,请参见此处。

    • 我正在尝试使用简单的自定义@NotNull注释对我的方法执行空检查,即我将方法声明为,当有人调用此方法并将空值作为'name'参数传递时,会引发异常。 我已经使用aspectj实现了一个简单方面。这个解决方案对我来说很有效。一个例外是内部类的构造函数。在这种情况下,由于java内部的异常,方面崩溃。郎。反思。参数: 简化的实施: 方面: 注释: 测试等级: 据我所知,这是由java将封闭的类对象作

    • 我在MyController类下有一个函数: 我在我的项目中设置了AspectJ,以便在这两个addPerson(…)时运行AOP逻辑上述方法称为: 我的问题在上述代码注释中提到。我想知道如何访问AOP函数中用RequestBody注释的参数?我不想检查参数类型或名称,但想知道如何通过检查注释来访问参数。有可能吗?

    • 问题内容: 我需要知道如何在运行时阅读Javadoc注释(可能是通过反射吗?) 说我有以下功能: 在运行时,我可以通过反射获得有关此函数的更多信息。但是无法阅读注释。所以问题是,如何在运行时阅读此 javadoc 注释。 问题答案: 考虑使用注释而不是Javadoc并编写注释处理器。

    • 问题内容: 是否可以通过注释处理器访问带有注释的元素? 是否可以通过注释处理器访问带注释的类型边界? 高度赞赏我错过的相关文档的链接。 内容: 注释: 一个示例类: 处理器: 在classpath 上使用编译以上内容将显示消息,但永远不会调用该方法。当方法参数中存在注释时,使用注释向处理器添加另一个注释可以正常工作。如果方法参数带有注释,则过程将再次忽略该元素。 问题答案: 该注释是有点棘手,因为

    • 我是Spring Security的新手。我看过很多关于如何通过外部属性文件的注释注入值的文章。我尝试了很多方法,但最终都是用java。lang.IllegalArgumentException:无法解析占位符“val.id”异常。 你能给我一些提示如何处理这个例外吗? 我的java类如下所示: 我的属性文件名为val.properties,位于WEB-INF下,其内容为val.id=xyz 我将