目录
一、什么是AOP
AOP英文全称:Aspect Oriented Programming(面向切面编程、面向方面编程),其实说白了,面向切面编程就是面向特定方法编程。使用场景(记录操作日志、权限控制、事务管理)。AOP的优势:减少重复代码、提高开发效率、维护方便、代码无侵入。
案例某部分功能运行较慢,定位执行耗时较长的业务方法,此时需要统计每一个业务的执行耗时。我们可以在方法开始前,获取方法开始时间,方法结束后获取结束时间,由此计算执行耗时。这个思路是正确的,但是过于繁琐。
动态代理是面向切面编程最主流的实现。而Spring AOP是Spring框架的高级技术,旨在管理bean对象的过程中,主要通过动态代理机制,对特定的方法进行编程(功能增强)。
二、Spring AOP快速入门
导入依赖:在pom.xml中导入AOP的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
编写AOP程序:针对特定方法根据业务需要进行编程
@Slf4j
@Component//把类交给spring容器进行管理
@Aspect//不是一个普通的类,aop类
public class TimeAspect {
@Around("execution(* com.example.demo.service.*.*(..))")//切入点表达式
public Object recordTime(ProceedingJoinPoint joinPoint) throws Throwable {
// 1.记录开始时间
long begin =System.currentTimeMillis();
// 2.调用原始方法运行
Object result=joinPoint.proceed();
// 3.记录结束时间,计算方法执行耗时
long end=System.currentTimeMillis();
log.info(joinPoint.getSignature()+"方法执行耗时:{}ms",end-begin);
return result;
}
}
三、AOP核心概念
连接点:JoinPoint,可以被AOP控制的方法(暗含方法执行时的相关信息),可以简单理解为可以被AOP控制的方法。
通知:Advice指定哪些可以重复的逻辑,也就是共性功能(最终被视为一个方法)
切入点:PointCut,匹配连接点的条件,通知仅会在切入点方法执行时被应用(满足条件的连接点)
切面:Aspect,描述通知与切入点对应的关系(通知+切入点)
目标对象:Target,通知所应用的对象
四、AOP的通知
1.Spring中AOP的通知类型
@Around:环绕通知,此注解标注的通知方法在目标方法前、后都被执行(重点)
@Before:前置通知,此注解标注的通知方法在目标方法前被执行
@After :后置通知,此注解标注的通知方法在目标方法后被执行,无论是否有异常都会执行
@AfterReturning : 返回后通知,此注解标注的通知方法在目标方法后被执行,有异常不会执行
@AfterThrowing : 异常后通知,此注解标注的通知方法发生异常后执行
@Pointcut("execution(* com.example.demo.service.impl.DeptServiceImpl.*(..))")
private void pt(){}
//前置通知
@Before("pt()")
public void before(JoinPoint joinPoint){
log.info("before ...");
}
//环绕通知
@Around("pt()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
log.info("around before ...");
//调用目标对象的原始方法执行
Object result = proceedingJoinPoint.proceed();
//原始方法如果执行时有异常,环绕通知中的后置代码不会在执行了
log.info("around after ...");
return result;
}
//后置通知
@After("pt()")
public void after(JoinPoint joinPoint){
log.info("after ...");
}
//返回后通知(程序在正常执行的情况下,会执行的后置通知)
@AfterReturning("pt()")
public void afterReturning(JoinPoint joinPoint){
log.info("afterReturning ...");
}
//异常通知(程序在出现异常的情况下,执行的后置通知)
@AfterThrowing("pt()")
public void afterThrowing(JoinPoint joinPoint){
log.info("afterThrowing ...");
}
@PointCut注解,该注解的作用是将公共的切入点表达式抽取出来,需要用到时引用该切入点表达式即可。需要注意的是:当切入点方法使用private修饰时,仅能在当前切面类中引用该表达式, 当外部其他切面类中也要引用当前类中的切入点表达式,就需要把private改为public,而在引用的时候,具体的语法为:
全类名.方法名(),具体形式如下:
@Before("com.itheima.aspect.MyAspect1.pt()")
在使用通知时的注意事项:
@Around环绕通知需要自己调用 ProceedingJoinPoint.proceed() 来让原始方法执行,其他通知不需要考虑目标方法执行
@Around环绕通知方法的返回值,必须指定为Object,来接收原始方法的返回值,否则原始方法执行完毕,是获取不到返回值的
2.通知执行顺序
在不同切面类中,默认按照切面类的类名字母排序:
目标方法前的通知方法:字母排名靠前的先执行
目标方法后的通知方法:字母排名靠前的后执行
如果我们想控制通知的执行顺序有两种方式:
修改切面类的类名(这种方式非常繁琐、而且不便管理)
使用Spring提供的@Order注解(注解加在切面类上)
@Order(2) //切面类的执行顺序(前置通知:数字越小先执行; 后置通知:数字越小越后执行)
3.切入点表达式
切入点表达式:
描述切入点方法的一种表达式
作用:主要用来决定项目中的哪些方法需要加入通知
常见形式:
execution(……):根据方法的签名来匹配
execution(访问修饰符? 返回值 包名.类名.?方法名(方法参数) throws 异常?)
其中带?
的表示可以省略的部分
@Before("execution(void com.itheima.service.impl.DeptServiceImpl.delete(java.lang.Integer))")
可以使用通配符描述切入点
*
:单个独立的任意符号,可以通配任意返回值、包名、类名、方法名、任意类型的一个参数,也可以通配包、类、方法名的一部分..
:多个连续的任意符号,可以通配任意层级的包,或任意类型、任意个数的参数
@annotation(……) :根据注解匹配
基于注解的方式来匹配切入点方法。这种方式虽然多一步操作,我们需要自定义一个注解,但是相对来比较灵活。我们需要匹配哪个方法,就在方法上加上对应的注解就可以
实现步骤:
编写自定义注解
在业务类要做为连接点的方法上添加自定义注解
4.连接点
在Spring中用JoinPoint抽象了连接点,用它可以获得方法执行时的相关信息,如目标类名、方法名、方法参数等。
对于@Around通知,获取连接点信息只能使用ProceedingJoinPoint类型
对于其他四种通知,获取连接点信息只能使用JoinPoint,它是ProceedingJoinPoint的父类型
//获取目标类名
String name = pjp.getTarget().getClass().getName();
log.info("目标类名:{}",name);
//目标方法名
String methodName = pjp.getSignature().getName();
log.info("目标方法名:{}",methodName);
//获取方法执行时需要的参数
Object[] args = pjp.getArgs();
log.info("目标方法参数:{}", Arrays.toString(args));
//执行原始方法
Object returnValue = pjp.proceed();
"Navigate To Advised Methods" 是一个在许多集成开发环境(IDE)中常见的功能,它允许开发者快速定位到被代理或增强的方法。这个功能通常在处理 AOP(面向切面编程)时非常有用,因为它可以帮助开发者更容易地找到和理解代码中的切面逻辑。