目录
在 Spring 开发中,切面(Aspect) 可以用来对方法进行统一的增强,比如日志记录、事务管理、性能监控等。本篇文章通过一个简单的 计算器案例,演示如何使用 Spring AOP 的 @Before
、@After
、@AfterThrowing
、@Around
等注解来实现切面。
1. 定义接口
新建一个 MathCalculator
接口,定义四个常见的数学运算方法。
📄 MathCalculator.java
package org.gdcp.spring01helloworld.MathCalculator;
public interface MathCalculator {
public int add(int i, int j);
public int sub(int i, int j);
public int ride(int i, int j);
public int div(int i, int j);
}
2. 接口实现类
在实现类中完成具体的运算逻辑,并用 @Component
注册到 Spring 容器。
📄 MathCalculatorImpl.java
package org.gdcp.spring01helloworld.MathCalculator.impl;
import org.gdcp.spring01helloworld.MathCalculator.MathCalculator;
import org.springframework.stereotype.Component;
@Component
public class MathCalculatorImpl implements MathCalculator {
@Override
public int add(int i, int j) {
System.out.println("执行加法...");
int result = i + j;
System.out.println("返回结果:" + result);
return result;
}
@Override
public int sub(int i, int j) {
System.out.println("执行减法...");
return i - j;
}
@Override
public int ride(int i, int j) {
System.out.println("执行乘法...");
return i * j;
}
@Override
public int div(int i, int j) {
System.out.println("执行除法...");
return i / j;
}
}
3. 引入 AOP 依赖
在 pom.xml
中添加 Spring AOP 的依赖:
<!-- AOP 切面依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>3.3.4</version>
</dependency>
4. 定义切面类
在切面类中,我们可以使用多种通知类型:
-
@Before
:方法执行前执行 -
@After
:方法执行后执行 -
@AfterReturning
:方法正常返回后执行 -
@AfterThrowing
:方法抛出异常后执行 -
@Around
:环绕通知(最强大,可以控制方法是否执行)
普通日志切面
📄 LogAspect.java
package org.gdcp.spring01helloworld.log;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class LogAspect {
@Before("execution(* org.gdcp.spring01helloworld.MathCalculator.impl.MathCalculatorImpl.*(..))")
public void logStart() {
System.out.println("运行开始...");
}
@After("execution(* org.gdcp.spring01helloworld.MathCalculator.impl.MathCalculatorImpl.*(..))")
public void logEnd() {
System.out.println("运行结束...");
}
@AfterReturning("execution(* org.gdcp.spring01helloworld.MathCalculator.impl.MathCalculatorImpl.*(..))")
public void logReturn() {
System.out.println("方法成功返回结果...");
}
@AfterThrowing("execution(* org.gdcp.spring01helloworld.MathCalculator.impl.MathCalculatorImpl.*(..))")
public void logError() {
System.out.println("方法运行出错...");
}
}
5. 使用环绕通知
环绕通知可以统计方法执行时间,常用于性能监控。
📄 LogAspect.java (环绕写法)
package org.gdcp.spring01helloworld.log;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class LogAspect {
// 定义切点
@Pointcut("execution(* org.gdcp.spring01helloworld.MathCalculator.impl.MathCalculatorImpl.*(..))")
public void pointCut() {}
// 环绕通知
@Around("pointCut()")
public Object AroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
long beginTime = System.currentTimeMillis();
System.out.println("环绕通知:方法执行前...");
// 执行目标方法
Object result = pjp.proceed();
long endTime = System.currentTimeMillis();
System.out.println("环绕通知:方法执行后...");
System.out.println("方法总耗时 = " + (endTime - beginTime) + " ms");
return result;
}
}
6. 测试类
在 SpringBoot 测试类中调用我们的计算器,观察切面效果。
📄 Spring01HelloworldApplicationTests.java
package org.gdcp.spring01helloworld;
import org.gdcp.spring01helloworld.MathCalculator.MathCalculator;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class Spring01HelloworldApplicationTests {
@Autowired
private MathCalculator mathCalculator;
@Test
public void test01() {
mathCalculator.add(1, 1);
mathCalculator.sub(5, 2);
mathCalculator.ride(2, 3);
mathCalculator.div(10, 2);
}
}
7. 运行效果
执行 test01
后,控制台输出示例(环绕通知 + 普通日志):
环绕通知:方法执行前...
运行开始...
执行加法...
返回结果:2
方法成功返回结果...
运行结束...
环绕通知:方法执行后...
方法总耗时 = 1 ms
如果出现异常,例如 mathCalculator.div(10, 0);
,则会触发 @AfterThrowing 通知,输出:
运行开始...
执行除法...
方法运行出错...
运行结束...
8. 总结
-
execution(...)
表达式格式为:
返回值类型 包名.类名.方法名(参数) -
@Before
、@After
、@AfterReturning
、@AfterThrowing
各自对应方法执行的不同阶段 -
@Around
是最强大的通知,可以包裹整个方法执行过程,常用于性能监控
通过这个案例,我们实现了对计算器所有方法的统一日志打印和执行耗时统计,完全不需要修改业务代码,这就是 AOP 的强大之处。