AOP,面向切面
定义:多业务流程的通用功能抽取并单独封装,形成独立的切面,在合适的时机将这些切面横向切入到业务流程指定的位置中
通知,5种类型:
-
Before
在方法被调用之前调用 -
After
在方法完成后调用通知,无论方法是否执行成功 -
After-returning
在方法成功执行之后调用通知 -
After-throwing
在方法抛出异常后调用通知 -
Around
通知了好、包含了被通知的方法,在被通知的方法调用之前后调用之后执行自定义的行为
切点,即系统中的方法,在哪被调用。在指定包、类的方法调用
<aop:pointcut id="point" expression="execution(* wokao666.club.aop.spring01.Subject.*(..))" />
切面(Aspect)
切面是切点和通知的集合,一般单独作为一个类。通知和切点共同定义了关于切面的全部内容,它是什么时候,在何时和何处完成功能。
AOP中的代理模式:https://www.cnblogs.com/q540973436/p/12656028.html
- 如果目标对象的实现类实现了接口,Spring AOP 将会采用 JDK 动态代理来生成 AOP 代理类;
- 如果目标对象的实现类没有实现接口,Spring AOP 将会采用 CGLIB 来生成 AOP 代理类——不过这个选择过程对开发者完全透明、开发者也无需关心。
1 2 3 4 5 6 7 8 9 10 | <aop:config> <!-- 这是定义一个切面,切面是切点和通知的集合--> <aop:aspect id= "do" ref= "PermissionVerification" > <!-- 定义切点 ,后面是expression语言,表示包括该接口中定义的所有方法都会被执行--> <aop:pointcut id= "point" expression= "execution(* wokao666.club.aop.spring01.Subject.*(..))" /> <!-- 定义通知 --> <aop:before method= "canLogin" pointcut-ref= "point" /> <aop:after method= "saveMessage" pointcut-ref= "point" /> </aop:aspect> </aop:config> |
然后说下Spring AOP支持的几种类似于@Before的AspectJ注解:
- 前置通知@Before: 前置通知通过@Before注解进行标注,并可直接传入切点表达式的值,该通知在目标函数执行前执行,注意JoinPoint,是Spring提供的静态变量,通过joinPoint 参数,可以获取目标对象的信息,如类名称,方法参数,方法名称等,该参数是可选的。
@Before("execution(...)")
public void before(JoinPoint joinPoint){
System.out.println("...");
}
- 后置通知@AfterReturning: 通过@AfterReturning注解进行标注,该函数在目标函数执行完成后执行,并可以获取到目标函数最终的返回值returnVal,当目标函数没有返回值时,returnVal将返回null,必须通过returning = “returnVal”注明参数的名称而且必须与通知函数的参数名称相同。请注意,在任何通知中这些参数都是可选的,需要使用时直接填写即可,不需要使用时,可以完成不用声明出来。
@AfterReturning(value="execution(...)",returning = "returnVal")
public void AfterReturning(JoinPoint joinPoint,Object returnVal){
System.out.println("我是后置通知...returnVal+"+returnVal);
}
- 异常通知 @AfterThrowing:该通知只有在异常时才会被触发,并由throwing来声明一个接收异常信息的变量,同样异常通知也用于Joinpoint参数,需要时加上即可.
@AfterThrowing(value="execution(....)",throwing = "e")
public void afterThrowable(Throwable e){
System.out.println("出现异常:msg="+e.getMessage());
}
复制代码
- 最终通知 @After:该通知有点类似于finally代码块,只要应用了无论什么情况下都会执行.
@After("execution(...)")
public void after(JoinPoint joinPoint) {
System.out.println("最终通知....");
}
- 环绕通知 @Around: 环绕通知既可以在目标方法前执行也可在目标方法之后执行,更重要的是环绕通知可以控制目标方法是否指向执行,但即使如此,我们应该尽量以最简单的方式满足需求,在仅需在目标方法前执行时,应该采用前置通知而非环绕通知。案例代码如下第一个参数必须是ProceedingJoinPoint,通过该对象的proceed()方法来执行目标函数,proceed()的返回值就是环绕通知的返回值。同样的,ProceedingJoinPoint对象也是可以获取目标对象的信息,如类名称,方法参数,方法名称等等
@Around("execution(...)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("我是环绕通知前....");
//执行目标函数
Object obj= (Object) joinPoint.proceed();
System.out.println("我是环绕通知后....");
return obj;
}
然后说下一直用"..."忽略掉的切入点表达式,这个表达式可以不是exection(..),还有其他的一些,我就不说了,说最常用的execution:
//scope :方法作用域,如public,private,protect
//returnt-type:方法返回值类型
//fully-qualified-class-name:方法所在类的完全限定名称
//parameters 方法参数
execution(<scope> <return-type> <fully-qualified-class-name>.*(parameters))
复制代码
<fully-qualified-class-name>.*(parameters)
注意这一块,如果没有精确到class-name,而是到包名就停止了,要用两个".."来表示包下的任意类:
- execution(* com.zdy..*(..)):com.zdy包下所有类的所有方法.
- execution(* com.zdy.Dog.*(..)): Dog类下的所有方法.
具体详细语法,大家如果有需求自行google了,我最常用的就是这俩了。要么按照包来定位,要么按照具体类来定位.
在使用切入点时,还可以抽出来一个@Pointcut来供使用:
/**
* 使用Pointcut定义切点
*/
@Pointcut("execution(...)")
private void myPointcut(){}
/**
* 应用切入点函数
*/
@After(value="myPointcut()")
public void afterDemo(){
System.out.println("最终通知....");
}
可以避免重复的execution在不同的注解里写很多遍...
https://juejin.im/post/5a55af9e518825734d14813f
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· winform 绘制太阳,地球,月球 运作规律
· 上周热点回顾(3.3-3.9)