一、AOP的概念
AOP(Aspect Oriented Programming),即面向切面编程。 (OOP:Object 面向对象编程)
有了AOP,你写代码时不需要把这个验证用户步骤写进去,即完全不考虑验证用户。只写取款和显示余额的业务代码。而在另一个地方,写好验证用户的代码。这个验证用户的代码就是切面代码,以后在执行取款和显示余额的时候,利用代理模式。将验证用户的功能在执行取款和显示余额前调用。代码在Spring容器中执行的时候,通过配置告诉Spring你要把这段代码加到哪几个地方,Spring就会在执行正常业务流程的时候帮你把验证代码和取款代码织入到一起。
AOP真正目的是:你写代码的时候,只需考虑主流程,而不用考虑那些不重要的,但又必须要写的其它相同的代码,这些其它的相同代码所在的类就是切面类。
常用场景:

二、AOP术语
JoinPoint(连接点):在程序执行过程中的某个阶段点,连接点就是指主业务方法的调用,它是客观存在的。
Pointcut(切入点):切入点指的是类或者方法名,满足某一规则的类或方法都是切入点,通过切入点表达式来制定规则。
Advice(通知):切入点处所要执行的程序代码,即切面类中要执行的公共方法。通知的类型有: 前置通知,后置通知,异常通知,最终通知,环绕通知。
Target(目标对象):被代理的对象。比如动态代理案例中的明星,房东。
Weaving(织入):织入指的是把新增的功能用于目标对象,创建代理对象的过程。
Proxy(代理):一个类被AOP织入增强后产生的结果类,即代理类。比如动态代理案例中的经纪人或中介
Aspect(切面):切面指的是切入点(规则)和通知(织入方法)的类 = 切入点+通知
三、执行流程分析
四、AOP常用标签和通知类型
环绕通知【重点】
五、AOP:注解方式实现
注解 | 说明 |
@Aspect | 用在类上,表示这是一个切面类 这个切面类要放到IoC容器中,所以类上还要加@Component注解 |
@Before | 用在方法上,表示这是一个前置通知 value:指定切入点表达式 |
@AfterReturning | 用在方法上,表示后置通知 |
@AfterThrowing | 用在方法上,表示异常通知 |
@After | 用在方法上,表示最终通知。注:在后置通知之前执行 |
@Around | 用在方法上,表示环绕通知 |
@Pointcut | 用在方法上,用来定义切入点表达式 方法名:随便起 返回值:void 方法体:为空 |
package com.itheima.utils;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* 记录日志功能的类:切面类 = 切入点(规则)+通知(方法)
*/
@Component
@Aspect //这是一个切面类
public class LogAspect {
//定义切入点函数
@Pointcut("execution(* com.itheima.service..*(..))")
public void pt() {
}
@Before("pt()") //写方法的名字
public void before() {
System.out.println("---前置通知---");
}
@AfterReturning("pt()")
public void afterReturning() {
System.out.println("---后置通知---");
}
@AfterThrowing("pt()")
public void afterThrowing() {
System.out.println("---异常通知---");
}
@After("pt()")
public void after() {
System.out.println("---最终通知---");
}
}
/**
* 环绕通知方法
* @param joinPoint 接口,由Spring注入对象
* @return 方法的返回值
*/
@Around("pt()")
public Object around(ProceedingJoinPoint joinPoint) {
//获取目标方法签名对象
Signature signature = joinPoint.getSignature();
System.out.println("目标方法名字:" + signature.getName());
//获取参数的数组
Object[] args = joinPoint.getArgs();
System.out.println("目标方法参数:" + Arrays.toString(args));
System.out.println("目标对象实现接口的全名:" + signature.getDeclaringTypeName());
Object result = null;
//修改目标方法参数
args[0] = "白骨精";
try {
System.out.println("前置通知");
//如果修改了参数,要使用带参数的方法。(如果不执行这句话,目标方法不会执行)
result = joinPoint.proceed(args); //直接调用目标方法
System.out.println("后置通知");
} catch (Throwable throwable) {
System.out.println("异常通知");
} finally {
System.out.println("最终通知");
}
return 99;
}