记录学完《苍穹外卖》的一些知识——AOP
目录
AOP:Aspect Oriented Programming(面向切面编程、面向方面编程)
AOP:Aspect Oriented Programming(面向切面编程、面向方面编程)
面向切面编程就是面向特定方法编程。
举个栗子:当我们开发了很多的功能后,我想统计每个方法的执行的时长,从而看看哪些功能需要优化,那么那么多方法,我难道要在每个方法开始时都记录时间,再在结束时再记录时间最后相减吗?这太繁琐了,那么能不能不修改原本的业务代码还能便捷的统计时间呢,当然是可以的,这时候就可以使用aop,比如:

只需要定义一个切面类,就这样就可以实现这个功能,而且还支持自由的修改作用的范围,也能精确定位到需要使用的位置。
所以,AOP的优势主要体现在以下四个方面:
-
减少重复代码:不需要在业务方法中定义大量的重复性的代码,只需要将重复性的代码抽取到AOP程序中即可。
-
代码无侵入:在基于AOP实现这些业务功能时,对原有的业务代码是没有任何侵入的,不需要修改任何的业务代码。
-
提高开发效率
-
维护方便
AOP有几个核心概念:连接点、通知、切入点、切面、目标对象
✨ 如果你能理解这些概念的含义和它们之间的关系,AOP 基本上就入门了!
1. 连接点(Join Point)
定义:程序执行过程中明确的点,比如 方法的调用、异常的抛出、字段的修改,这些都是连接点。
Spring AOP 限制:Spring AOP 只支持方法级别的连接点(也就是方法调用)。
举例:一个 UserService 里有 login()、register() 方法,这两个方法都是连接点。
✅ 连接点是你“有机会”织入增强逻辑的地方。
2. 切入点(Pointcut)
定义:切入点是对连接点的筛选规则。也就是说:你选中了哪些连接点进行增强操作。
通俗理解:连接点是所有的方法,切入点是你“感兴趣”的那些方法。
常见语法:
@Pointcut("execution(* com.sky.service..*.*(..))")
这表示:匹配 com.sky.service 包及子包下的所有方法。
切入点 ≈ 哪些方法需要被增强。
3. 通知(Advice)
定义:你到底“在这些方法前后做了什么事”。
通知就是增强逻辑本身,比如:
-
记录日志(Log)
-
权限校验
-
参数校验
-
异常处理
-
事务控制
类型有五种:
| 类型 | 描述 |
|---|---|
@Before | 方法执行前执行 |
@AfterReturning | 方法成功执行后执行 |
@AfterThrowing | 方法抛出异常后执行 |
@After | 无论方法是否异常,最终都会执行 |
@Around | 最强!方法执行的前后你都可以控制 |
@Slf4j
@Component
@Aspect
public class MyAspect1 {
//前置通知
@Before("execution(* com.itheima.service.*.*(..))")
public void before(JoinPoint joinPoint){
log.info("before ...");
}
//环绕通知
@Around("execution(* com.itheima.service.*.*(..))")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
log.info("around before ...");
//调用目标对象的原始方法执行
Object result = proceedingJoinPoint.proceed();
//原始方法如果执行时有异常,环绕通知中的后置代码不会在执行了
log.info("around after ...");
return result;
}
//后置通知
@After("execution(* com.itheima.service.*.*(..))")
public void after(JoinPoint joinPoint){
log.info("after ...");
}
//返回后通知(程序在正常执行的情况下,会执行的后置通知)
@AfterReturning("execution(* com.itheima.service.*.*(..))")
public void afterReturning(JoinPoint joinPoint){
log.info("afterReturning ...");
}
//异常通知(程序在出现异常的情况下,执行的后置通知)
@AfterThrowing("execution(* com.itheima.service.*.*(..))")
public void afterThrowing(JoinPoint joinPoint){
log.info("afterThrowing ...");
}
}
✅ 通知 = 我们到底要织入什么功能逻辑。
4. 切面(Aspect)
定义:切面是一个模块化的横切关注点,包含切入点 + 通知。
简单理解就是:一个切面类,把“哪些方法需要增强” + “增强的内容”组合在一起。
@Aspect
@Component
public class LogAspect {
@Pointcut("execution(* com.sky.service..*.*(..))")
public void serviceMethods() {}
@Before("serviceMethods()")
public void beforeLog(JoinPoint joinPoint) {
System.out.println("方法执行前打印日志:" + joinPoint.getSignature());
}
}
✅ 切面 = 切入点 + 通知的集合,是 Spring AOP 的主角!
5. 目标对象(Target Object)
定义:你真正要增强的“业务对象”,比如 UserServiceImpl。
Spring 会基于它创建代理对象(AOP 代理),增强功能会织入到这个代理对象中。
✅ 目标对象 = 被切面增强的类。
概念总结图
通知(Advice) ---> 你要做什么(增强逻辑)
↑
切面(Aspect)= 切入点 + 通知
↓
切入点(Pointcut) ---> 你对哪些方法做增强
↓
连接点(JoinPoint)---> 程序里所有可能被增强的点
↓
目标对象(Target) ---> 被增强的类(比如 UserServiceImpl)
举个生活中的例子帮助理解
比如你是个“智能门卫系统”:
-
👁️🗨️ 所有住户都可以进出大门(连接点)
-
🚪 你只想记录晚上 10 点以后的出入(切入点)
-
📸 你在这些人经过时拍照/记录(通知)
-
📦 这个系统打包成了一个完整模块(切面)
-
👤 被监控的人就是住户本人(目标对象)
✅ 总结:
| 概念 | 一句话解释 |
|---|---|
| Join Point(连接点) | 程序中可以被增强的所有点(方法) |
| Pointcut(切入点) | 你选中了哪些方法 |
| Advice(通知) | 你想干什么(增强逻辑) |
| Aspect(切面) | 通知 + 切入点的合集 |
| Target Object(目标对象) | 被增强的业务类 |
不同类型的通知(Advice)在 Spring AOP 中,参数列表是有要求的,尤其是:
-
你要使用
JoinPoint、ProceedingJoinPoint -
想获取方法的返回值或异常信息
-
想访问注解、目标类、参数等信息
✅ 1. @Before 前置通知
方法签名要求:
@Before("切点表达式")
public void 方法名(JoinPoint joinPoint)
参数说明:
| 参数 | 类型 | 说明 |
|---|---|---|
JoinPoint | org.aspectj.lang.JoinPoint | 可选参数,用来获取方法名、参数等信息 |
✅ 示例:
@Before("execution(* com.sky.service.*.*(..))")
public void beforeAdvice(JoinPoint joinPoint) {
System.out.println("方法名:" + joinPoint.getSignature().getName());
System.out.println("参数:" + Arrays.toString(joinPoint.getArgs()));
}
✅ 2. @AfterReturning 返回后通知
方法签名要求:
@AfterReturning(value = "切点表达式", returning = "result")
public void 方法名(JoinPoint joinPoint, Object result)
参数说明:
| 参数 | 类型 | 说明 |
|---|---|---|
JoinPoint | 可选 | 方法调用的上下文信息 |
result | 必须有,名称要和注解里的 returning 一致 | 返回值 |
✅ 示例:
@AfterReturning(value = "execution(* com.sky.service.*.*(..))", returning = "result")
public void afterReturningAdvice(JoinPoint joinPoint, Object result) {
System.out.println("返回结果:" + result);
}
✅ 3. @AfterThrowing 异常通知
方法签名要求:
@AfterThrowing(value = "切点表达式", throwing = "ex")
public void 方法名(JoinPoint joinPoint, Throwable ex)
参数说明:
| 参数 | 类型 | 说明 |
|---|---|---|
JoinPoint | 可选 | 方法上下文 |
ex | Throwable 或其子类 | 抛出的异常对象,名称要和 throwing 对应 |
✅ 示例:
@AfterThrowing(value = "execution(* com.sky.service.*.*(..))", throwing = "ex")
public void afterThrowingAdvice(JoinPoint joinPoint, Exception ex) {
System.out.println("发生异常:" + ex.getMessage());
}
✅ 4. @After 最终通知
方法签名要求:
@After("切点表达式")
public void 方法名(JoinPoint joinPoint)
参数说明:
| 参数 | 类型 | 说明 |
|---|---|---|
JoinPoint | 可选 | 获取方法名、参数等 |
✅ 示例:
@After("execution(* com.sky.service.*.*(..))")
public void afterAdvice(JoinPoint joinPoint) {
System.out.println("方法执行完毕:" + joinPoint.getSignature().getName());
}
✅ 5. @Around 环绕通知
方法签名要求:
@Around("切点表达式")
public Object 方法名(ProceedingJoinPoint joinPoint) throws Throwable
参数说明:
| 参数 | 类型 | 说明 |
|---|---|---|
ProceedingJoinPoint | 是 JoinPoint 的子类,必须是唯一参数 | 可通过 proceed() 控制方法执行 |
✅ 示例:
@Around("execution(* com.sky.service.*.*(..))")
public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("方法执行前:" + joinPoint.getSignature().getName());
Object result = joinPoint.proceed(); // 调用目标方法
System.out.println("方法执行后:" + result);
return result;
}
⚠️ 注意事项总结
| 通知类型 | 是否可以写 JoinPoint | 是否可以获取返回值 | 是否可以获取异常 | 是否可以阻止执行 |
|---|---|---|---|---|
@Before | ✅ | ❌ | ❌ | ❌ |
@AfterReturning | ✅(可选) | ✅(需配置 returning) | ❌ | ❌ |
@AfterThrowing | ✅(可选) | ❌ | ✅(需配置 throwing) | ❌ |
@After | ✅(可选) | ❌ | ❌ | ❌ |
@Around | ❌(只能是 ProceedingJoinPoint) | ✅ | ✅ | ✅ |
关于切入点表达式可自行搜索相关资料,在此不多赘述
注意:本文引用了黑马程序员javaweb+ai的文档的内容
更多推荐



所有评论(0)