在 Spring AOP 中的After advice - @After

在 Spring AOP 中的After advice - @After

简介

在本文中,您将了解 Spring AOP 中的 After 通知,它在匹配的 Joinpoint 退出执行后运行。After 通知是使用 @After 注释声明的。它也被称为 After finally 通知,因为无论是否成功退出或由于异常而中断,它都会被调用。

您已经了解了上一篇文章中列出的 5 种建议类型,如下所示。

After (finally) advice execution – @After

After 通知也称为 After finally 通知,因为通知被执行,方法要么成功退出,要么通过抛出异常终止其执行。因此,After 通知必须准备好处理正常以及异常返回条件。一个简单的例子如下所示。

@After("execution(* com.jbd.saop.after.dao.*.*(..))")
  public void logResults(JoinPoint joinPoint){
    System.out.println("\nAfter advice executed: "
        + joinPoint.getSignature().toShortString());
  }

它通常用于释放资源和类似目的。

After throwing 通知示例 – @AfterThrowing

问题陈述——假设我们有一个用户管理应用程序。我们想要记录方法抛出的所有异常。我们将使用上一篇文章中的相同示例,并将空值传递给 add() 和 delete() 方法。

步骤 1:添加依赖项

创建一个maven项目,在pom.xml中添加如下依赖

<dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>5.2.2.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.2.RELEASE</version>
    </dependency>
    <!-- Step-1 Add the dependencies -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>5.2.2.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
     <version>1.9.5</version>
    </dependency>
<dependencies>

步骤 2:创建用户存储库

我们将切面应用到 DAO 类。所以我们创建了示例代码——UserRepository 和 Java 配置来注册这个类,如下所示。正如我们之前讨论过的,注解@EnableAspectJAutoProxy用于启用Spring-AOP。

UserRepository.java

package com.jbd.saop.after.dao;
import org.springframework.stereotype.Repository;
//A very stupid demo repository
@Repository
public class UserRepository {
  //Add a user
  public UserRepository add(String username){
    if(username == null) {
      throw new RuntimeException("username is null", new NullPointerException());
    }
    System.out.println("New user added: " + username);
    return this;
  }
  //Update an user
  public UserRepository update(String username, String email) {
    System.out.println("Update email: " + email);
    return this;
  }
  //Delete an user
  public boolean delete(String username){
    if (username == null) {
      throw new RuntimeException("username is null", new NullPointerException());
    }
    System.out.println("User deleted: " + username);
    return true;
  }
}

ApplicationConfig.java

package com.jbd.saop.after;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@EnableAspectJAutoProxy
@ComponentScan("com.jbd.saop.after")
public class ApplicationConfig {
}

步骤 3:创建切入点表达式和通知

切入点表达式匹配 Dao 类的所有方法。如果任何 Dao 类方法抛出 RuntimeException,它将与两个通知匹配。但是,如果任何方法成功退出,则只会执行 logResults()。

package com.jbd.saop.after.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Service;
@Service
@Aspect
public class AllAfterAspects {
  @Pointcut("execution(* com.jbd.saop.after.dao.*.*(..))")
  private void allDaoMethods(){}
  @AfterThrowing(
      pointcut = "allDaoMethods()",
      throwing = "exception")
  public void logDaoExceptions(JoinPoint joinPoint, RuntimeException exception) {
    System.out.println("After Exception executed: "
        + joinPoint.getSignature().toShortString());
    System.out.println("Exception details: " + exception);
  }
  @After("allDaoMethods()")
  public void logResults(JoinPoint joinPoint){
    System.out.println("\nAfter advice executed: "
        + joinPoint.getSignature().toShortString());
  }
}

第 4 步:测试示例

最后,我们将测试该示例以查看其输出。在运行测试用例之前,在 pom.xml 中添加测试依赖项。

<dependencies>
  ... 
   <!-- Test Dependencies-->
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-api</artifactId>
      <version>5.5.2</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-engine</artifactId>
      <version>5.5.2</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>5.2.2.RELEASE</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
...
<!-- Important for Jupiter-engine -->
<build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>3.0.0-M3</version>
      </plugin>
    </plugins>
  </build>
</project>

永远记得在使用 Junit-jupiter-* 时将 maven-surefire-plugin 升级到 3.0.0-M3 或更高版本。

添加测试依赖项后,在 src\test 中创建 Test 类。

package com.jbd.saop.after;
import com.jbd.saop.after.dao.UserRepository;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
@SpringJUnitConfig(ApplicationConfig.class)
public class TestAfterAdvice {
  @Autowired
  private UserRepository userRepository;
  @Test
  public void testAfterThrows() {
    //Assert exception
    Assertions.assertThrows(RuntimeException.class, () -> {
      userRepository.add(null);
      userRepository.delete(null);
    });
  }
  @Test
  public void testAfter() {
    //Assert not exception
    Assertions.assertDoesNotThrow(() -> {
      userRepository.delete("alexa");
    });
  }
}

测试用例应通过控制台中的以下输出。顺序可能不同。

输出

After advice executed: UserRepository.add(..)
After Exception executed: UserRepository.add(..)
Exception details: java.lang.RuntimeException: username is null

User deleted: alexa
After advice executed: UserRepository.delete(..)

正如您在输出中看到的,当 add() 方法抛出 RuntimeException 时,会调用两个建议(@After 和 @AfterThrowing)。只有 @After 建议在 delete() 完成执行后运行。

我希望能让你对 Spring AOP 中的 @After – After Advice 有一个很好的理解。在实际用例中,您可能希望使用它来执行资源释放类型的工作。

版权声明:著作权归作者所有。

相关推荐

MySQL删除重复的行,保留其中一行

示例有这样一个表CREATE TABLE `tests` (  `id` int(11) DEFAULT NULL,  `name` varchar(20) DEFAULT NULL ) ; 现在需要删除重复name的行,但保留其中一行。方法一:保留id小的一行DELETE n1 

Bootstrap在图片内居中显示文字

图片的父容器position设置为relative。文字position设置为absolute。.thumbnail {   position: relative; } .caption {   position: absolute;  top: 45%;  left: 0;  &nbs

ES6简化版的JavaScript中间件模式的实现

JavaScript中间件模式的目的是分解前端业务逻辑,通过next方法层层传递给下一个业务。比较经典的是express和koa。这是使用ES6实现的一个简版的中间件模式:class Middleware { use(fn) { this.go = (stack => next => stack(fn.bind(this, next.bind(this))))(this.go)

Pandas统计dataframe列中为NaN的行数

这分为两种情况:缺少值NaN和字符串NaN。缺少值NaNdf = pd.DataFrame({'value':[np.nan, np.nan, 1, 5, 7]})print (df) value0 NaN1 NaN2 1.03 5.04 7.0count = df['value'].isna().sum()#或者 count = df['valu

二叉树的中序遍历

中序遍历 中序遍历首先遍历左子树,然后访问根结点,最后遍历右子树。在遍历左、右子树时,仍然先遍历左子树,再访问根结点,最后遍历右子树。            R / A B / / C D E F 上面这个树的中序遍历顺

ES6中async的使用案例

在项目中有时会遇到异步操作的问题,async就是解决异步操作的终极操作。我会以终极三问(what,why,when)的形式来说明什么是async。由于这是第一篇文章不知道怎么写,有很大部分是借鉴阮一峰老的原文,事例将会从我的项目中摘取。 async是什么? 官方例子 官方文档 async相当于对Generator 函数的一个语法糖const fs = require

Raku根据index删除字符串中的字符

示例说明:0123456789,删除索引为1-3,以及8的字符串,结果应该为:045679。实现my $a='0123456789';with $a {$_=.comb[(^* ∖ (1..3, 8).flat).keys.sort].join};say $a;可以简写为一行代码:say '0123456789'.comb[(^* ∖ (1..3, 8).flat).keys.sort].join

Spring Cloud集成ZooKeeper注册中心绑定指定ip,解决UnknownHostException

Spring Cloud集成Zookeeper作为注册中心,从网关Spring Cloud GateWay转发给微服务里,在微服务里看到发送请求过来是以主机别名为url,如主机别名是server1.cluster,那么访问的地址是http://server.cluster/xxx。在GateWay有时会导致找不到服务。报类似如下的错误:500 Server Error for HTTP GET "