概念
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
我的理解:
首先我要说一句,反射技术的出现,是AOP产生的技术基础。
先说说软件的革命吧;
面向结构编程:函数是主力。
面向对象:对象成了主力。
前两次革命的优点我就不说了,那么,现实的生活中我们有这么一种情况。
无论类呀,函数啊,怎么的设计,总有一个方面的共性是分布在不同的对象和函数中的。
就比如提交表单吧,这是应应用系统几乎很普通的一个功能,这个表单的提交是在不同类中进行,
虽然都叫表单提交,但是他不属于任何一个对象,甚至不属于任何一类对象。
如果我们想对表单的提交动作都做一些相应的处理,就需要把机能插在这个切面上,
如果我们通过继承的角度来插入这个切面,可能复杂度,和灵活性都是不可接受的,
这时候,我们可以通过反射的技术,把一个机能引入到这个切面。这想比继承要容易的多吧。
并且我们要在系统中控制的不一定只这一个切面,可能有很多,这样的话用面向对象的技术几乎是解决不了的。
所以有了这么个概念,面向切面编程,这是从系统中提取的一个比抽象更抽象的面,应该说和对象不在一个维度。
如果把面向对象编程比作横向空间,那么面向切面的编程就叫纵向空间,这样整个编程变得多维和立体了。
系统的复用度,会变的更高,冗余(重复)更小,规划的空间更大。
作用
如果说面向对象编程是关注将需求功能划分为不同的并且相对独立,封装良好的类,并让它们有着属于自己的行为,依靠继承和多态等来定义彼此的关系的话;
那么面向切面编程则是希望能够将通用需求功能从不相关的类当中分离出来,能够使得很多类共享一个行为,一旦发生变化,不必修改很多类,而只需要修改这个行为即可。
面向切面编程,和面向对象编程的互补
面向对象编程主要用于为同一对象层次的公用行为建模。
它的弱点是将公共行为应用于多个无关对象模型之间(不同类的对象,没有继承关系的对象间也有公共属性,公共行为)。
而这恰恰是面向切面编程适合的地方。
有了 AOP,我们可以定义交叉的关系,并将这些关系应用于跨模块的、彼此不同的对象模型。
关联链接
1.AOP 面向切面编程
AOP(Aspect-Oriented Programming,面向切面编程)是一种编程范式,旨在通过将横切关注点(Cross-Cutting Concerns)与业务逻辑分离,提高代码的模块化、可维护性和可复用性。它与OOP(面向对象编程)互补,专注于解决传统编程中难以模块化的“横向”问题。
核心概念
横切关注点(Cross-Cutting Concerns)
指在多个模块中重复出现的非业务逻辑,例如:
日志记录(Log)
事务管理(Transaction)
安全校验(Security)
性能监控(Performance Monitoring)
这些逻辑“横切”多个业务模块,若直接嵌入业务代码会导致代码冗余和结构混乱。
关键术语
切面(Aspect)
封装横切关注点的模块,例如一个“日志切面”或“事务切面”。
连接点(Join Point)
程序执行过程中的某个特定点,例如方法调用、异常抛出等。
切入点(Pointcut)
定义一组连接点的集合,用于指定切面应作用的位置(如“所有以service结尾的方法”)。
通知(Advice)
切面在连接点上执行的具体逻辑,分为5种类型:
Before:连接点执行前触发
After:连接点执行后触发(无论成功或失败)
After Returning:连接点成功执行后触发
After Throwing:连接点抛出异常时触发
Around:包裹连接点,可控制其执行(如拦截方法调用)
织入(Weaving)
将切面逻辑与业务代码结合的过程,分为3种方式:
编译时织入(如AspectJ)
加载时织入(如Spring AOP)
运行时动态代理(如JDK动态代理)
AOP vs. OOP
OOP 聚焦“垂直”模块化,通过类和继承封装业务逻辑。
AOP 聚焦“水平”模块化,通过切面封装横切关注点。
互补关系:两者结合可实现更清晰的代码结构,例如用OOP实现业务逻辑,用AOP实现日志和事务管理。
实现方式
Spring AOP
基于动态代理(JDK Proxy或CGLIB),仅支持方法级别的切面。
示例:通过@Aspect注解定义切面,结合@Pointcut指定切入点。
java
@Aspect
public class LoggingAspect {
@Before(“execution(* com.example.service..(…))”)
public void logBefore(JoinPoint joinPoint) {
System.out.println("Before method: " + joinPoint.getSignature());
}
}
AspectJ
更强大的AOP框架,支持编译时织入和更复杂的切入点表达式。
示例:通过@Aspect和@Pointcut定义切面。
java
@Aspect
public class PerformanceAspect {
@Around(“execution(* com.example….(…))”)
public Object measurePerformance(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
Object result = pjp.proceed();
System.out.println("Execution time: " + (System.currentTimeMillis() - start) + “ms”);
return result;
}
}
应用场景
日志与监控:在方法调用前后记录日志或统计性能。
事务管理:自动开启、提交或回滚数据库事务。
安全控制:校验用户权限或防止SQL注入。
缓存管理:在方法执行前检查缓存,执行后更新缓存。
错误处理:统一捕获异常并发送报警。
优缺点
优点:
分离横切关注点,减少代码重复。
提高模块化和可维护性。
业务逻辑更清晰,专注核心功能。
缺点:
学习曲线(需理解切面、通知等概念)。
过度使用可能导致代码难以调试(切面逻辑分散)。
性能开销(动态代理或编译时织入可能影响启动速度)。
学习建议
基础:理解AOP核心概念(切面、通知、织入)。
实践:通过Spring AOP或AspectJ实现简单切面(如日志记录)。
深入:研究AOP在事务管理、安全控制等场景的应用。
工具:结合IDE(如IntelliJ)调试切面逻辑,确保织入正确。
通过AOP,开发者可以更优雅地解决横切关注点问题,使代码更简洁、可维护,尤其在复杂企业级应用中发挥重要作用。
2.举例说明
🍳 更形象的例子:用AOP给“煎蛋”过程添加“计时”和“安全检查”切面
🎯 目标
用AOP实现:不修改煎蛋的主流程代码,通过切面自动添加“计时”和“安全检查”功能。
📝 代码实现(Python + 装饰器模拟AOP)
python
import time
----------------------
主业务逻辑:煎蛋
----------------------
class EggFrying:
def fry_egg(self, style):
print(f"🍳 开始煎{style}蛋…“)
time.sleep(1) # 模拟煎蛋过程
print(f"🍳 {style}蛋煎好了!”)
return f"一份完美的{style}蛋!"
----------------------
AOP切面:计时 + 安全检查
----------------------
def egg_safety_aspect(func):
def wrapper(*args, **kwargs):
# 前置通知:安全检查
print(“🔍 安全检查:确认锅已预热、油温合适…”)
# 执行主逻辑
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
# 后置通知:计时
print(f"⏱️ 煎蛋耗时:{end_time - start_time:.2f}秒")
return result
return wrapper
----------------------
将切面“织入”主逻辑
----------------------
egg = EggFrying()
egg.fry_egg = egg_safety_aspect(egg.fry_egg) # 动态代理“织入”
----------------------
运行结果
----------------------
print(egg.fry_egg(“荷包蛋”))
🖥️ 执行结果
plaintext
🔍 安全检查:确认锅已预热、油温合适…
🍳 开始煎荷包蛋…
🍳 荷包蛋煎好了!
⏱️ 煎蛋耗时:1.00秒
一份完美的荷包蛋!
💡 关键点解析
主业务逻辑(煎蛋)
EggFrying类只关注煎蛋本身,代码干净简洁,没有混入安全检查或计时逻辑。
切面逻辑(安全检查 + 计时)
egg_safety_aspect装饰器封装了两个横切关注点:
Before通知:煎蛋前执行安全检查(🔍)。
After通知:煎蛋后计算耗时(⏱️)。
通过装饰器“织入”主逻辑,无需修改fry_egg方法。
AOP的优势
解耦:安全检查和计时逻辑与煎蛋流程完全分离,可复用到其他烹饪方法(如煎牛排)。
集中维护:修改切面逻辑(如调整安全检查规则)无需改动主业务代码。
🚀 更进一步:多个切面组合
假设还需添加“操作记录”切面,只需再定义一个装饰器:
python
def operation_log_aspect(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
print(“📝 操作记录:用户执行了煎蛋操作”)
return result
return wrapper
织入多个切面(先安全检查,再记录日志)
egg.fry_egg = egg_safety_aspect(operation_log_aspect(egg.fry_egg))
执行结果:
plaintext
🔍 安全检查:确认锅已预热、油温合适…
🍳 开始煎荷包蛋…
🍳 荷包蛋煎好了!
📝 操作记录:用户执行了煎蛋操作
⏱️ 煎蛋耗时:1.00秒
通过AOP,多个横切关注点可以像“叠buff”一样灵活组合!