实现自定义Spring AOP注解 - 极悦
专注Java教育14年 全国咨询/投诉热线:444-1124-454
极悦LOGO图
始于2009,口口相传的Java黄埔军校
首页 hot资讯 实现自定义Spring AOP注解

实现自定义Spring AOP注解

更新时间:2022-06-13 09:33:48 来源:极悦 浏览1072次

在本文中,我们将使用 Spring 中的 AOP 支持来实现自定义 AOP 注释。

首先,我们将给出 AOP 的高级概述,解释它是什么以及它的优点。在此之后,我们将逐步实现我们的注解,逐步建立对 AOP 概念的更深入理解。

结果将是对 AOP 的更好理解以及未来创建自定义 Spring 注释的能力。

1. 什么是 AOP 注解?

快速总结一下,AOP 代表面向方面的编程。从本质上讲,它是一种无需修改代码即可向现有代码添加行为的方法。

关于 AOP 的详细介绍,有关于 AOP切入点和建议的文章。本文假设我们已经具备基本知识。

我们将在本文中实现的 AOP 类型是注解驱动的。如果我们使用过 Spring @Transactional注解,我们可能已经对此很熟悉了:

@Transactional
public void orderGoods(Order order) {
   // A series of database calls to be performed in a transaction
}

这里的关键是非侵入性。通过使用注释元数据,我们的核心业务逻辑不会被我们的事务代码污染。这使得推理、重构和隔离测试变得更容易。

有时,开发 Spring 应用程序的人可以将其视为“ Spring Magic”,而无需详细考虑它是如何工作的。实际上,发生的事情并不是特别复杂。但是,一旦我们完成了本文中的步骤,我们将能够创建自己的自定义注解,以便理解和利用 AOP。

2. Maven依赖

首先,让我们添加我们的Maven依赖机制

对于这个例子,我们将使用 Spring Boot,因为它的约定优于配置的方法让我们能够尽快启动并运行:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.2.RELEASE</version>
</parent>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
</dependencies>

请注意,我们已经包含了 AOP 启动器,它引入了我们开始实现方面所需的库。

3. 创建我们的自定义注解

我们要创建的注释将用于记录方法执行所需的时间。让我们创建我们的注解:

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

尽管实现相对简单,但值得注意的是这两个元注释的用途。

@Target注解告诉我们注解适用的地方。 这里我们使用的是ElementType.Method,这意味着它只适用于方法。如果我们尝试在其他任何地方使用注解,那么我们的代码将无法编译。这种行为是有道理的,因为我们的注释将用于记录方法执行时间。

@Retention只是说明注解在运行时是否对 JVM 可用。默认情况下它不是,所以 Spring AOP 将无法看到注解。这就是它被重新配置的原因。

4. 创造我们的方面

现在我们有了注释,让我们创建我们的方面。这只是将封装我们的横切关注点的模块,在我们的例子中是方法执行时间日志记录。它只是一个类,用@Aspect注释:

@Aspect
@Component
public class ExampleAspect {
}

我们还包含了@Component 注释,因为我们的类也需要是一个 Spring bean 才能被检测到。本质上,这是我们将实现我们希望自定义注解注入的逻辑的类。

5. 创建我们的切入点和建议

现在,让我们创建我们的切入点和建议。这将是一个存在于我们方面的带注释的方法:

@Around("@annotation(LogExecutionTime)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
    return joinPoint.proceed();
}

从技术上讲,这并没有改变任何东西的行为,但是仍然有很多事情需要分析。

首先,我们用@Around注释了我们的方法。这是我们的建议,围绕建议意味着我们在方法执行之前和之后添加额外的代码。还有其他类型的建议,例如之前和之后,但它们将超出本文的范围。

接下来,我们的@Around注释有一个切入点参数。我们的切入点只是说,“将此建议应用于任何带有@LogExecutionTime注释的方法。” 还有很多其他类型的切入点,但如果作用域,它们将再次被排除在外。

logExecutionTime()方法本身就是我们的建议。有一个参数ProceedingJoinPoint。在我们的例子中,这将是一个使用@LogExecutionTime 注释的执行方法。

最后,当我们的注解方法最终被调用时,会发生的是我们的通知将首先被调用。然后由我们的建议决定下一步该做什么。在我们的例子中,我们的建议除了调用proceed()之外什么都不做,它只是调用原始的带注释的方法。

6. 记录我们的执行时间

现在我们已经有了我们的骨架,我们需要做的就是在我们的建议中添加一些额外的逻辑。除了调用原始方法之外,这将是记录执行时间的内容。让我们将这个额外的行为添加到我们的建议中:

@Around("@annotation(LogExecutionTime)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
    long start = System.currentTimeMillis();
    Object proceed = joinPoint.proceed();
    long executionTime = System.currentTimeMillis() - start;
    System.out.println(joinPoint.getSignature() + " executed in " + executionTime + "ms");
    return proceed;
}

同样,我们在这里没有做任何特别复杂的事情。我们刚刚记录了当前时间,执行了方法,然后将花费的时间打印到控制台。我们还记录了方法签名,它是为使用连接点实例而提供的。如果我们愿意,我们还可以访问其他信息,例如方法参数。

现在,让我们尝试用@LogExecutionTime 注释一个方法,然后执行它来看看会发生什么。请注意,这必须是 Spring Bean 才能正常工作:

@LogExecutionTime
public void serve() throws InterruptedException {
    Thread.sleep(2000);
}

执行后,我们应该会看到控制台记录了以下内容:

void org.baeldung.Service.serve() executed in 2030ms

以上就是关于“实现自定义Spring AOP注解”介绍,大家如果对此比较感兴趣,想了解更多相关知识,不妨来关注一下极悦的Spring教程,里面的课程内容由浅到深,细致全面,很适合没有基础的小伙伴学习,希望对大家能够有所帮助哦。

提交申请后,顾问老师会电话与您沟通安排学习

免费课程推荐 >>
技术文档推荐 >>