aop 获取注解注释的方法_带有AOP和注释的Java方法记录

aop 获取注解注释的方法

有时,我想记录(通过slf4jlog4j )方法的每次执行,查看其接收的参数,返回的内容以及每次执行花费的时间。 这是我在AspectJjcabi-aspects和Java 6注释的帮助下所做的事情:

public class Foo {
  @Loggable
  public int power(int x, int p) {
    return Math.pow(x, p);
  }
}

这是我在log4j输出中看到的:

[INFO] com.example.Foo #power(2, 10): 1024 in 12μs
[INFO] com.example.Foo #power(3, 3): 27 in 4μs

很好,不是吗? 现在,让我们看看它是如何工作的。

带有运行时保留的注释

注释是Java 6中引入的一种技术。它是一种元编程工具,它不会改变代码的工作方式,但会为某些元素(方法,类或变量)提供标记。 换句话说,注释只是附加到可以查看和阅读的代码的标记。 有些注释仅在编译时可见-编译后在.class文件中不存在。 其他的在编译后仍然可见,并且可以在运行时访问。

例如, @Override是第一种类型(其保留类型是SOURCE ),而来自JUnit的@Test是第二种类型(保留类型是RUNTIME )。 @Loggable (我在上面的脚本中使用的那个)是jcabi-aspects中第二种类型的注释。 编译后,它与.class文件中的字节码保持在一起。

同样,重要的是要理解,即使对power()方法进行了注释和编译,它到目前为止也不会向slf4j发送任何内容。 它仅包含一个标记,上面写着“请记录我的执行情况”。

面向方面的编程(AOP)

AOP是一种有用的技术,它允许在不显式更改源代码的情况下将可执行块添加到源代码。 在我们的示例中,我们不想在类内部记录方法的执行情况。 相反,我们希望其他类拦截对方法power()每次调用,测量其执行时间,然后将此信息发送给slf4j。

我们希望该拦截器理解我们的@Loggable批注并记录对该特定方法power()每次调用。 而且,当然,相同的拦截器应用于将来我们将放置相同批注的其他方法。

这种情况完全符合AOP的初衷-避免在多个类中重新实现某些常见行为。

日志记录是我们主要功能的补充功能,我们不想使用多个日志记录指令来污染我们的代码。 相反,我们希望在后台进行日志记录。

就AOP而言,我们的解决方案可以解释为创建一个方面 ,该方面在某些连接点横切代码,并应用环绕建议以实现所需的功能。

纵横比

让我们看看这些魔术词的含义。 但是,首先,让我们看一下jcabi-aspects如何使用AspectJ实现它们(这是一个简化的示例,您可以在MethodLogger.java找到完整的代码):

@Aspect
public class MethodLogger {
  @Around("execution(* *(..)) && @annotation(Loggable)")
  public Object around(ProceedingJoinPoint point) {
    long start = System.currentTimeMillis();
    Object result = point.proceed();
    Logger.info(
      "#%s(%s): %s in %[msec]s",
      MethodSignature.class.cast(point.getSignature()).getMethod().getName(),
      point.getArgs(),
      result,
      System.currentTimeMillis() - start
    );
    return result;
  }
}

这是一个方面 ,里面有一个关于 around() 建议 。 方面用@Aspect注释,而建议用@Around注释。 如上所述,这些注释只是.class文件中的标记。 除了为那些对运行时感兴趣的人提供一些元信息外,他们什么也不做。

注释@Around有一个参数,在这种情况下,该参数表示在以下情况下建议应适用于方法:

  1. 其可见性修饰符为*publicprotectedprivate );
  2. 它的名字是名字* (任何名字);
  3. 它的参数是.. (任何参数); 和
  4. 它用@Loggable注释

如果要截获对带注释的方法的调用,则在执行实际方法之前,先执行方法around() 。 当要拦截对power()方法的调用时, around()方法将接收ProceedingJoinPoint类的实例,并且必须返回一个对象,该对象将作为power()方法的结果使用。

为了调用原始方法power() ,建议必须调用连接点对象的proceed()

我们编译此方面,并使其与主文件Foo.class一起在classpath中可用。 到目前为止,一切都很好,但是我们需要采取最后一步,以便将我们的方面付诸实践-我们应该应用我们的建议。

二元方面编织

方面编织是建议应用过程的名称。 Aspect Weaver通过注入对Aspect的调用来修改原始代码。 AspectJ正是这样做的。 我们给它提供了两个二进制Java类Foo.classMethodLogger.class ; 它给退三-修改Foo.classFoo$AjcClosure1.class和未修改MethodLogger.class

为了了解哪些建议应应用于哪些方法,AspectJ weaver使用了.class文件中的注释。 同样,它使用反射来浏览类路径上的所有类。 它通过@Around注释分析哪些方法满足条件。 当然,它找到我们的方法power()

因此,有两个步骤。 首先,我们使用javac编译.java文件,并获得两个文件。 然后,AspectJ编织/修改它们并创建自己的额外类。 编织后,我们的Foo类如下所示:

public class Foo {
  private final MethodLogger logger;
  @Loggable
  public int power(int x, int p) {
    return this.logger.around(point);
  }
  private int power_aroundBody(int x, int p) {
    return Math.pow(x, p);
  }
}

AspectJ weaver将我们的原始功能移至新方法power_aroundBody() ,并将所有power()调用重定向到方面类MethodLogger

现在,我们有四个类一起工作,而不是Foo类中的power()方法。 从现在开始,这就是每次调用power()幕后发生的事情:

未命名

方法power()原始功能由图中的绿色小生命线指示。

如您所见,方面编织过程将类和方面连接在一起,并通过连接点在它们之间转移调用。 无需编织,类和方面都只是带有附加注释的已编译Java二进制文件。

jcabi方面

jcabi-aspects是一个JAR库,其中包含Loggable注释和MethodLogger方面(顺便说一句,还有更多方面和注释)。 您无需编写自己的方面来记录方法。 只需在类路径中添加一些依赖项,然后配置jcabi-maven-plugin进行编织(在Maven Central中获取其最新版本):

<project>
  <depenencies>
    <dependency>
      <dependency>
        <groupId>com.jcabi</groupId>
        <artifactId>jcabi-aspects</artifactId>
      </dependency>
      <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjrt</artifactId>
      </dependency>
    </dependency>
  </depenencies>
  <build>
    <plugins>
      <plugin>
        <groupId>com.jcabi</groupId>
        <artifactId>jcabi-maven-plugin</artifactId>
        <executions>
          <execution>
            <goals>
              <goal>ajc</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>

由于此编织过程需要大量的配置工作,因此我创建了一个具有ajc目标的便捷Maven插件,该插件可以完成整个方面的编织工作。 您可以直接使用AspectJ,但是我建议您使用jcabi-maven-plugin

而已。 现在,您可以使用@com.jcabi.aspects.Loggable注释,您的方法将通过slf4j记录。

如果某些内容无法按照说明进行操作,请立即提交Github问题

相关文章

您可能还会发现以下有趣的帖子:

翻译自: https://www.javacodegeeks.com/2014/09/java-method-logging-with-aop-and-annotations.html

aop 获取注解注释的方法

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值