Spring AOP 学习
1.什么是AOP
AOP:(Aspect Oriented Programming)面向切面编程,用于将某些与业务无关,但却对多个对象产生影响的公共行为和逻辑从业务逻辑中抽取出来,并封装为一个可重用的模块(类似于vue中的组件【个人见解】)其本质为在不改变原有业务逻辑的情况下增强横切逻辑。
AOP的出现可以说是为了完善OOP(面向对象编程)【补充:OOP是一种垂直继承体系,其特征是封装、继承、多态】
下图为OOP的简约演示图,可见非业务性代码会产生多次重复,这里的圈红的代码可以称为横切逻辑代码
下图为AOP的演示图,利用代理对象将业务逻辑代码与横切逻辑代码分离开来
2.AOP相关术语
目标对象(Target):代理的目标对象
需要添加公共功能的原对象,被一个或多个切面所通知的对象
代理(Proxy):产生的一个结果代理类
一个类被AOP织入增强后,就产生一个结果代理类
连接点(Join point):方法
这些点指方法,一个连接点代表一个方法的执行,因为spring只支持方法类型的连接点,连接点是在执行过程中插入切面的一个点,切面可以利用这些点插入到正常流程之中,并添加新的行为
切入点(Pointcut):添加公共功能的位置
切入点定义会匹配通知所要织入的连接点(定义一个方法,关联原有业务逻辑代码和所需要实现AOP功能的相应方法)
通知/增强(Advice):添加的公共功能
切面的工作即为通知(需要在连接点实现的具体功能),指拦截连接点之后所要做的事情
切面(Aspect):通知+切入点
通知和切入点共同定义了切面的内容(实现切面功能的那个类)
织入(Weaving):在目标对象上使用切面
织入是把切面应用到目标对象并创建新的代理对象的过程
3.切入点表达式的写法
定义切入点时要使用@Pointcut注解@Pointcut(value =execution(…))
语法:
execution([修饰符]返回值类型包名.类名.方法名(参数))
例子:
execution(public void com.iflytek.aop.Target.method())
execution() :表达式主体;
public : 访问修饰符(可以省略);
void : 返回值类型;
com.iflytek.aop :包名(可以使用*号代表任意);
Target : 类名(可以使用*号代表任意);
method :方法名(可以使用*号代表任意);
- 包名与类名之间一个点.代表当前包下的类,两个点…表示当前包及其子包下的类
- 参数列表可以使用两个点…表示任意个数,任意类型的参数列表
4.AOP的代理
AOP实现的关键在于代理模式,AOP的代理主要分为静态代理和动态代理
静态代理:(编译时增强)AOP框架会在编译阶段生成AOP代理类,在编译阶段将切面织入java字节码中,运行的时候已经是增强的AOP对象了
动态代理:不会去修改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象(包含了目标对象的全部方法),在特定的切点做增强处理,并回调原对象的方法
两种常用动态代理
JDK代理(基于接口)
使用的是反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
将被代理类作为构造函数的参数传入代理类proxy,调用Proxy.newProxyInsatnce(classloader,interfaces,handler)方法生成代理类,实现Invocation接口,重写invoke()方法,调用被代理类方法时默认调用此方法
JDK动态代理只能对实现了接口的类生成代理,而不能针对类
cglib代理(基于父类)
通过动态的生成一个子类(常用Enhancer)去覆盖所要代理的类,Enhancer既能够代理普通的class,也能够代理接口,Enhancer创建一个被代理对象的子类并且拦截所有的方法调用
CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法因为是继承(该类不声明成final)
两个代理的选择
目标对象实现了接口=>默认采用JDK动态代理
目标对象为未实现接口=>一般采用cglib代理,也可采用JDK代理
关于JDK和CGLIB两种动态代理事例详情可见:JDK和CGLIB动态代理_星空彼岸007的博客-CSDN博客
5.通知的类型
5.1 spring切面可以应用的5种类型的通知(切面的工作被称为通知):
名称 | 标签 | 注解 | 说明 |
---|---|---|---|
前置通知 | <aop:before> | @Before | 指定增强的方法在切入点方法之前执行的通知 |
后置通知 | <aop:AfterReturning> | @AfterReturning | 指定增强的方法在切入点方法之后执行的通知,又叫返回后通知 |
环绕通知 | <aop:Around> | @Around | 指定增强的方法在切入点方法之前和之后都执行的通知 |
异常抛出通知 | <aop:AfterThrowing> | @AfterThrowing | 指定增强的方法在出现异常时执行的通知 |
最终通知 | <aop:After> | @After | 无论增强方式执行是否有异常都会执行的通知 |
5.2 通知的配置语法:
<!--配置文件方式-->
<aop:通知类型 method="切面类中方法名" pointcut="切点表达式"></aop:通知类型>
//注解方式
@通知注解("切点表达式")
5.3 五种通知常见的使用场景
前置通知---->记录日志(方法将被调用)
环绕通知---->控制事务、权限控制
后置通知---->记录日志(方法已经成功调用)
异常通知---->异常处理、控制事务
最终通知---->记录日志(方法已经调用,但不一定成功)
注意:环绕通知的执行顺序是优于普通通知的,多个切面类的执行顺序是按照切面类的名称的首字母进行排序操作的,若需要人为规定,则需要在切面类上添加@Order注解同时加上具体的值,值越小,越优先。
异常处理、控制事务
最终通知---->记录日志(方法已经调用,但不一定成功)
注意:环绕通知的执行顺序是优于普通通知的,多个切面类的执行顺序是按照切面类的名称的首字母进行排序操作的,若需要人为规定,则需要在切面类上添加@Order注解同时加上具体的值,值越小,越优先。
本次学习笔记部分来源于该原文链接:https://blog.csdn.net/m0_37741420/article/details/124522376