AOP (Aspect-Oriented Programming) ,即面向切面编程,可以通过预编译方式和运行期动态代理实现在不修改代码的情况下给程序动态统一添加功能的一种技术。是面向对象编程(OOP)的一种补充。
AOP 编程思想,把多类对象中的横切问题点,从业务逻辑中分离出来,从而达到解耦的目的,增加代码重用性,提高开发效率。
AOP 主要概念理解
- aspect:切面,切面由切点和通知组成,即包括横切逻辑的定义也包括连接点的定义
- pointcut:切点,每个类都拥有多个连接点,可以理解为连接点的集合
- joinpoint:连接点,程序执行的某个特定位置,如某个方法调用前后等
- weaving:织入,将增强添加到目标类的具体连接点的过程
- advice:通知,是织入到目标类连接点上的一段代码,就是增强到什么地方?增强什么内容?
- target:目标对象,通知织入后产生的对象
AOP 底层实现,是通过 JDK 动态代理或 CGLib 代理在运行时期在对象初始化阶段织入代码的。
JDK动态代理基于接口实现——————CGLib 是基于类的继承实现
通知的种类
在切面类内主要代码
- Before advice
- 前置通知,即在目标方法调用之前执行。注意:即无论方法是否遇到异常都执行
- After returning advice
- 后置通知,在目标方法执行后执行,前提是目标方法没有遇到异常,如果有异常则不执行通知
- After throwing advice
- 异常通知,在目标方法抛出异常时执行,可以获取异常信息
- After finally advice
- 最终通知,在目标方法执行后执行,无论是否是异常执行
- Around advice
- 环绕通知,最强大的通知类型,可以控制目标方法的执行,可以在目标执行全过程中进行执行。
如何写一个切面类
- 定义一个切面类 Aspect
- 即在声明的类,增加@Component 和 @Aspect 两个注解,Springboot 中要引入
spring-boot-stater-aop
依赖包 - @Component 用来告诉 Spring 容器要扫描这个类
- @Aspect 的作用是声明该类是一个切面类
- 即在声明的类,增加@Component 和 @Aspect 两个注解,Springboot 中要引入
- 定义切点 Pointcut——通知增强代码在哪些类和方法进行执行
- 定义切点,并定义切点在那些地方执行,采用 @pointcut 注解完成。
- 如@Pointcut(public * com.xxx.xxx..(…))
- 上述规则:修饰符(可以不写,但不能用*) + 返回类型 + 那些包下的类 + 那些方法 + 方法参数
- “*”代表不限,“…"两个点代表参数不限
- 定义 Advice 通知
- 利用通知的 5 种类型注解 @Before、@After、@AfterReturning、@AfterThrowing、@Around 来完成在某些切点的增强动作
- 如 @Before(myPointcut()),myPointcut 为第二步骤定义的切点
AOP案例代码
定义一个日志切面类 Aspect
@Aspect
@Component
public class LoggingAdvice{
private Logger logger = LoggerFactory.getLogger(LoggingAdvice.class);
}
定义切点 Pointcut
/**
* 切点名称:myPointcut ,在返回类型不限的com.spring.aop.controller包下的所有类,所有方法,
并且参数不限
*/
@Pointcut( value="execution( * com.spring.aop.controller.*.*(..))")
public void myPointcut(){
}
定义 Advice 通知
//注意: ProceedingJoinPoint 只能用于环绕通知,因为 ProceedingJoinPoint 暴露了 proceed 方法
//下面的代码因为有这个注解,所以都会增强到myPointcut()该方法
@Around("myPointcut()")
public Object applicationLogger(ProceedingJoinPoint pjp) throws Throwable{
//获取方法名
String methodName = pjp.getSignature().getName();
//获取类名
String className = pjp.getTarget().getClass().toString();
//获取参数
ObjectMapper m = new ObjectMapper();
Object[] array = pjp.getArgs();
//打印调用该方法时,参数是什么
logger.info("调用前:"+className+":"+methodName+" args= "+m.writeValueAsString(array));
//打印调用方法后,产生的结果是什么
Object obj = pjp.proceed();
logger.info("调用后:"+className +":"+methodName+" 返回 "+m.writeValueAsString(obj));
}
写完这些之后,在com.spring.aop.controller包下的所有类,所有方法进行调用后,日志都会在控制台进行打印。