After Throwing advice in Spring AOP – @AfterThrowing

简介
匹配的方法通过抛出异常完成/中止其执行后调用抛出通知之后。您已经知道,这种匹配方法的名称是 Joinpoint。您可以使用 @AfterThrowing 注释声明 After throwing advice。
您已经了解了上一篇文章中列出的 5 种建议类型,如下所示。
- Before advice –
@Before
- After returning –
@AfterReturning
- After throwing –
@AfterThrowing
- After (finally) advice –
@After
- Around advice –
@Around
After throwing advice execution
您可以声明一个 After throwing 通知,如下所示。或者,您可以获取方法执行引发的异常和 Joinpoint 详细信息。
package com.jbd.saop.after.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Service;
@Service
@Aspect
public class AllAfterAspects {
@AfterThrowing(
pointcut = "execution(* c.jbd.saop.at.dao.*.*(..))",
throwing = "exception")
public void logDaoExceptions(JoinPoint joinPoint, RuntimeException exception) {
System.out.println("Exception at: " + joinPoint.getSignature().toShortString());
System.out.println("Exception details: " + exception);
}
}
抛出子句还将匹配限制为仅抛出指定类型异常的那些方法执行。在这种情况下,它是 RuntimeException。
After throwing advice示例 – @AfterThrowing
问题陈述——假设我们有一个用户管理应用程序。我们想要记录方法抛出的所有异常。我们将使用上一篇文章中的相同示例,并将空值传递给 add() 和 delete() 方法。
步骤 1:添加依赖项
<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 c.jbd.saop.at.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 c.jbd.saop.at;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@EnableAspectJAutoProxy
@ComponentScan("c.jbd.saop.at")
public class ApplicationConfig {
}
步骤 3:创建切入点表达式和通知
切入点表达式与所有 Dao 类方法匹配。如果 Dao 类方法中的任何一个抛出 RuntimeException,它将与这个切面进行匹配,并执行 logDaoExceptions() 中的代码。
package c.jbd.saop.at.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.context.annotation.Configuration;
@Configuration
@Aspect
public class ExceptionAspect {
/**
* Match with all dao methods.
*/
@AfterThrowing(
pointcut = "execution(* c.jbd.saop.at.dao.*.*(..))",
throwing = "exception")
public void logDaoExceptions(JoinPoint joinPoint, RuntimeException exception) {
System.out.println("Exception at: " + joinPoint.getSignature().toShortString());
System.out.println("Exception details: " + exception);
}
}
第 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 c.jbd.saop.at;
import c.jbd.saop.at.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 TestAfterThrowing {
@Autowired
private UserRepository userRepository;
@Test
public void notNull() {
Assertions.assertNotNull(userRepository);
}
@Test
public void testThrows() {
//Assert throws RuntimeException
Assertions.assertThrows(RuntimeException.class, () -> {
userRepository.add(null);
userRepository.delete(null);
});
//Assert does not throw exception
Assertions.assertDoesNotThrow(() -> {
userRepository.delete("alexa");
});
}
}
测试用例应通过控制台中的以下输出。
我希望已经让您对@AfterThrowing – Spring AOP 中的投掷后建议有一个很好的理解。在实际用例中,您可能希望使用它对异常执行日志或分析。
After method - Exception at: UserRepository.add(..)
Exception details: java.lang.RuntimeException: username is null
User deleted: alexa