Spring AOP原理
一、定义
AOP(Aspect Oriented Programming),即面向切面编程,是OOP(面向对象编程)的一种延续,二者互补。
AOP的目的是将横切关注点(如日志记录、事务管理、权限控制、接口限流等)从核心业务中分离出来形成一个个切面(Aspect),通过动态管理、字节码操作等技术实现代码的复用和解耦,提高代码的可维护性和可扩展性。
二、关键术语
- 横切关注点(cross-cutting concerns):多个类或对象中的公共行为(如日志记录、事务管理、权限控制、接口限流、接口幂等等)。
- 切面(Aspect):对横切关注点进行封装的类,一个切面是一个类。切面可以定义多个通知,用来实现具体的功能。
- 连接点(JoinPoint):连接点是方法调用或者方法执行时的某个特定时刻(如方法调用、异常抛出等)。
- 通知(Advice):通知就是切面在某个连接点要执行的操作。通知有五种类型,分别是前置通知(Before)、后置通知(After)、返回通知(AfterReturning)、异常通知(AfterThrowing)和环绕通知(Around)。前四种通知都是在目标方法的前后执行,而环绕通知可以控制目标方法的执行过程。
- 切点(Pointcut):一个切点是一个表达式,它用来匹配哪些连接点需要被切面所增强。切点可以通过注解、正则表达式、逻辑运算等方式来定义。比如
execution(* com.xyz.service..*(..))
匹配com.xyz.service
包及其子包下的类或接口。 - 织入(Weaving):织入是将切面和目标对象连接起来的过程,也就是将通知应用到切点匹配的连接点上。常见的织入时机有两种,分别是编译期织入(Compile-Time Weaving 如:AspectJ)和运行期织入(Runtime Weaving 如:AspectJ、Spring AOP)。
三、常见的通知类型
- Before(前置通知):目标对象的方法调用之前触发。
- After(后置通知):目标对象的方法调用之后触发。
- AfterReturning(返回通知):目标对象的方法调用完成,在返回结果值之后触发。
- AfterThrowing(异常通知):目标对象的方法运行中抛出/触发异常后触发,AfterReturning和AfterThrowing两者互斥。如果方法调用成功无异常,则会有返回值;如果方法抛出了异常,则不会有返回值。
- Around(环绕通知):编程式控制目标对象的方法调用。环绕通知是所有通知类型中可操作范围最大的一种,因为它可以直接拿到目标对象,以及要执行的方法,所以环绕通知可以任意的在目标对象的方法调用前后搞事,甚至不调用目标对象的方法
四、应用场景
OOP不能很好地处理一些分散在多个类或对象中的公共行为,这些行为通常被称为横切关注点。如果我们在每个类或对象中都重复实现这些行为,那么会导致代码的冗余、复杂和难以维护,所以才有了AOP。
下面举个简单的例子:
假设你有两个方法:
1 | public void createUser1(String name) { |
很显然需要挨个写逻辑很烦,而且还全是重复的代码段。使用AOP技术之后,我们可以将重复的逻辑封装成一个切面,然后通过切入点和通知来指定在哪些方法需要执行指定的操作。
1 |
|
然后在原方法上加一行注解@Log
即可实现重复的操作。
五、实现方式
一是很经典的动态代理(JDK Proxy和CGLIB Proxy),二是字节码操作(AspectJ)。
动态代理之前的文章已经有讲过这里不再详细讲解,值得一提的是Spring的代理策略和SpringBoot是不一样的:
- Spring AOP:如果要代理的对象,实现了某个接口,那么会使用JDK Proxy,去创建代理对象,而对于没有实现接口的对象,会使用CGLIB生成一个被代理对象的子类来作为代理。
- SpringBoot:Spring Boot2.0之前,默认使用JDK动态代理。2.0开始后,如果用户什么都不配置的话,默认使用CGLIB动态代理。
Spring AOP属于运行时增强,而AspectJ是编译时增强。
怎么理解上面这句话呢,意思就是Spring AOP使用的是动态代理,是在运行的时候对方法进行拦截而没有改变原有类。而AspectJ是把增强部分的代码直接”写入”了原来的类,也就是说原来的类生成的二进制文件里面就已经包括了增强后的代码,已经被修改过了。
评论