Advise Methods on Annotated Classes With AspectJ – 用AspectJ对注解类进行提示方法

最后修改: 2021年 4月 23日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’ll use AspectJ to write trace logging output when calling methods of configured classes. By using an AOP advice to write trace logging output, we encapsulate the logic into a single compilation unit.

在本教程中,我们将使用AspectJ来编写调用配置类的方法时的跟踪日志输出。通过使用AOP建议来编写跟踪日志输出,我们将逻辑封装在一个单一的编译单元中。

Our example expands upon the information presented in Intro to AspectJ.

我们的示例对Intro to AspectJ中介绍的信息进行了扩展。

2. Trace Logging Annotation

2.跟踪记录注释

We’ll use an annotation to configure classes so their method calls can be traced. Using an annotation gives us an easy mechanism for adding the trace logging output to new code without having to add logging statements directly.

我们将使用注解来配置类,以便它们的方法调用可以被追踪。使用注解为我们提供了一种简单的机制,可以将追踪日志输出添加到新的代码中,而不必直接添加日志语句。

Let’s create the annotation:

让我们来创建注释。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Trace {
}

3. Creating Our Aspect

3.创立我们的观点

We’ll create an aspect to define our pointcut to match the join points we care about and the around advice containing the logic to execute.

我们将创建一个方面来定义我们的pointcut,以匹配我们关心的连接点和包含执行逻辑的around建议。

Our aspect will look similar to this:

我们的方面将看起来与此类似。

public aspect TracingAspect {
    private static final Log LOG = LogFactory.getLog(TracingAspect.class);

    pointcut traceAnnotatedClasses(): within(@Trace *) && execution(* *(..));

    Object around() : traceAnnotatedClasses() {
        String signature = thisJoinPoint.getSignature().toShortString();
        LOG.trace("Entering " + signature);
        try {
            return proceed();
        } finally {
            LOG.trace("Exiting " + signature);
        }
    }
}

In our aspect, we define a pointcut named traceAnnotatedClasses to match the execution of methods within classes annotated with our Trace annotation. By defining and naming a pointcut, we can reuse it as we would a method in a class. We’ll use this named pointcut to configure ours around advice.

在我们的方面,我们定义了一个名为traceAnnotatedClassespointcut,以匹配Trace注解的类中的方法的执行通过定义和命名一个pointcut,我们可以像重用类中的方法一样重用它。我们将使用这个命名的pointcut来配置我们的around建议。

Our around advice will execute in place of any join point matched by our pointcut and will return an Object. By having an Object return type, we can account for advised methods having any return type, even void.

我们的around建议将代替任何由我们的pointcut匹配的连接点执行,并将返回一个Object通过拥有一个Object返回类型,我们可以考虑建议方法的任何返回类型,甚至是void

We retrieve the signature of the matched join point to create a short String representation of the signature to add context to our tracing messages. As a result, our logging output will have the name of the class and the method executed, which gives us some needed context.

我们检索匹配的连接点的签名,以创建签名的简短String表示,为我们的跟踪消息添加上下文。因此,我们的日志输出将有类的名称和执行的方法,这给了我们一些需要的背景。

In between our trace output calls, we’ve called a method named proceed. This method is available for around advice in order to continue the execution of the matched join point. The return type will be Object since we have no way to know the return type at compile time. We will send this value back to the caller after sending the final trace output to the log.

在我们的跟踪输出调用之间,我们已经调用了一个名为proceed的方法。这个方法可用于around建议,以便继续执行匹配的连接点。返回类型将是Object,因为我们没有办法在编译时知道返回类型。我们将在把最后的跟踪输出发送到日志后,把这个值送回给调用者。

We wrap the proceed() call in a try/finally block to ensure the exit message is written. If we wanted to trace the thrown exception, we could add after() advice to write a log message when an exception is thrown:

我们将proceed()调用包裹在一个try/finally块中,以确保退出信息被写入。如果我们想追踪被抛出的异常,我们可以添加after()建议,在抛出异常时写入一条日志信息。

after() throwing (Exception e) : traceAnnotatedClasses() {
    LOG.trace("Exception thrown from " + thisJoinPoint.getSignature().toShortString(), e);
}

4. Annotating Our Code

4.对我们的代码进行注释

Now we need to enable our trace. Let’s create a simple class and activate the trace logging with our custom annotation:

现在我们需要启用我们的跟踪。让我们创建一个简单的类,用我们的自定义注解激活跟踪记录。

@Trace
@Component
public class MyTracedService {

    public void performSomeLogic() {
        ...
    }

    public void performSomeAdditionalLogic() {
        ...
    }
}

With the Trace annotation in place, the methods in our class will match the pointcut we’ve defined. When these methods execute, the tracing messages will be written to the log.

有了Trace注解,我们类中的方法将与我们定义的pointcut匹配。当这些方法执行时,跟踪信息将被写到日志中。

After running our code calling these methods, our log output should include content similar to:

在运行我们调用这些方法的代码后,我们的日志输出应该包括类似于以下内容:

22:37:58.867 [main] TRACE c.b.a.c.TracingAspect - Entering MyTracedService.performSomeAdditionalLogic()
22:37:58.868 [main] INFO  c.b.a.c.MyTracedService - Inside performSomeAdditionalLogic...
22:37:58.868 [main] TRACE c.b.a.c.TracingAspect - Exiting MyTracedService.performSomeAdditionalLogic()
22:37:58.869 [main] TRACE c.b.a.c.TracingAspect - Entering MyTracedService.performSomeLogic()
22:37:58.869 [main] INFO  c.b.a.c.MyTracedService - Inside performSomeLogic...
22:37:58.869 [main] TRACE c.b.a.c.TracingAspect - Exiting MyTracedService.performSomeLogic()

5. Conclusion

5.总结

In this article, we used AspectJ to intercept all of a class’s methods with a single annotation on the class. Doing so allows us to quickly add our trace logging functionality to new code.

在这篇文章中,我们使用AspectJ,用类上的一个注解拦截了该类的所有方法。这样做使我们能够快速地将我们的跟踪记录功能添加到新的代码中。

We also consolidated our trace logging output logic to a single compilation unit to improve our ability to modify our trace logging output as our application evolves.

我们还将我们的跟踪记录输出逻辑合并到一个单一的编译单元,以提高我们在应用发展过程中修改跟踪记录输出的能力。

As always, the full source code of the article is available over on GitHub.

一如既往,文章的完整源代码可在GitHub上获得