1. 概述

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

我们的示例扩展了AspectJ 简介中提供的信息。

2. 跟踪记录注释

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

让我们创建注释:

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

3. 创建我们的方面

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

我们的方面将类似于:

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

在我们的方面,我们定义了一个名为 traceAnnotatedClasses切入点 ,以匹配使用 Trace 注释注释的类 方法的 执行通过定义和命名 切入点, 我们可以像重用类中的方法一样重用它。 我们将使用这个命名 切入点围绕 建议配置我们的切入点。

我们的 周围 建议将代替与我们的 切入点 匹配的任何连接点执行,并将返回一个 Object 通过具有 Object 返回类型,我们可以考虑具有任何返回类型的建议方法,甚至是 void

我们检索匹配连接点的签名,以创建签名的短 字符串 表示形式,以将上下文添加到我们的跟踪消息中。因此,我们的日志输出将包含类的名称和执行的方法,这为我们提供了一些所需的上下文。

在跟踪输出调用之间,我们调用了一个名为 proceed 的方法。 此方法可用于 周围 通知,以便继续执行匹配的连接点。 返回类型将是 Object ,因为我们无法在编译时知道返回类型。在将最终跟踪输出发送到日志后,我们将将此值发送回调用者。

我们将 proceed() 调用包装在 try / finally 块中,以确保写入退出消息。 如果我们想跟踪抛出的异常,我们可以添加 after() 建议,以便在抛出异常时写入日志消息:

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

4. 注释我们的代码

现在我们需要启用我们的跟踪。让我们创建一个简单的类并使用自定义注释激活跟踪日志记录:

@Trace
@Component
public class MyTracedService {

    public void performSomeLogic() {
        ...
    }

    public void performSomeAdditionalLogic() {
        ...
    }
}

使用 Trace 注释后,我们类中的方法将与我们定义的 切入点 相匹配。当这些方法执行时,跟踪消息将被写入日志。

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

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. 结论

在本文中,我们使用 AspectJ 通过类上的单个注释来拦截类的所有方法。这样做使我们能够快速将跟踪日志记录功能添加到新代码中。

我们还将跟踪日志记录输出逻辑合并到单个编译单元,以提高随着应用程序的发展修改跟踪日志记录输出的能力。

与往常一样,本文的完整源代码可以在 GitHub 上获取。