AOP 面试题总结
基础与概念
AOP 概述
AOP 面向切面编程,全称 Aspect Oriented Programming,它是对 OOP 技术的补充。OOP 关注的核心是对象,AOP 的核心是切面(Aspect)。AOP 可以再不修改功能代码的前提下,使用运行时动态代理技术对已有的代码逻辑增强。AOP 可以实现组件化、可插拔式的功能扩展,通过简单的配置可以将功能增强到指定的切入点。
AOP 的设计原理
AOP 的底层设计是由运行时动态代理支撑,在 bean 的初始化流程中,借助 BeanPostProcessor 将原始的目标对象织入通知,生成代理对象。
OR
AOP 的设计原理是对原有业务逻辑的横切增强,使用不同的通知方式,它有不同的底层原理支持(编译期、类加载器、对象创建期)
JDK 动态代理和 Cglib 动态代理对比
- JDK 动态代理要求被代理对象所属类至少实现一个接口,它是 JDK 内置的机制
- Cglib 动态代理没有接口限制,使用字节码增强技术实现,需要依赖第三方依赖包
- JDK 动态代理的代理对象创建速度快,执行速度慢;Cglib 动态代理的代理对象创建速度慢,执行速度快。
AOP 的术语
- Target 目标对象:被代理的原始对象
- Proxy 代理对象:目标对象被织入通知后产生的对象就是代理对象
- JoinPoint 连接点:目标对象所属类中定义的所有方法均为连接点
- Pointcut 切入点:被切面拦截 / 增强的连接点(切入点一定是连接点,连接点不一定是切入点)
- Advice 通知:增强的逻辑 / 代码,也就是拦截到目标对象的连接点之后要做的事
- Aspect 切面:切入点 + 通知
- Weaving 织入:将通知应用到目标对象,进而生成代理对象的过程动作
- Introduction 引介:特殊的通知类型,可以不修改原有代码的前提下,在运行期间为原始类动态的添加新的属性 / 方法
AspectJ定义的通知类型
- Before 前置通知:目标对象的方法调用后触发
- After 后置通知:目标对象的方法调用只会触发
- AfterRunning 返回通知:目的对的方法调用完成,在返回结果之前触发
- AfterThrowing 异常通知:目标对象的方法中抛出 / 触发异常后触发
- Around 环绕通知:编程式控制目标对象的方法调用。环绕通知式所有通知类型中可操作范围最大的一种,因为它可以直接拿到目标对象,以及要执行的方法,所以环绕通知可以任意的在目标对象的方法前后搞事情,甚至不执行目标对象的方法
这些通知类型中 AfterRunning 和 AfterThrowing 两者是互斥的!如果方法调用成功无异常抛出,则会有返回值;如果方法抛出了异常,则不会有返回值。
AOP 联盟定义的通知类型
- 前置通知 MethodBeforeAdvice
- 后置通知(返回通知) AfterRunningAdvice
- 异常通知 ThrowsAdvice
- 环绕通知 MethodInterceptor
- 引介通知 IntroductionAdvice
AOP 的编码与应用
AOP 使用场景
AOP 的使用范围是非常的广的
- 业务日志切面:可以记录业务方法的调用痕迹
- 事务控制:通过切面可以实现声明式事务控制
- 权限校验:执行方法之前先校验当前登陆用户是否用于相应权限
- 数据缓存:执行方法之前先从缓存中获取,取到则直接返回,不走业务方法;没取到执行业务方法,拿到数据后在方法返回之前,将数据缓存
多个切面的执行顺序如何控制
- 显示使用
@Order
注解,或者Ordered
接口,声明切面的执行顺序(默认Integer.MAX_VALUE) - 通过使用类名的 unicode 编码排序,控制切面的执行顺序
AOP 的失效场景
- 代理对象使用 this 调用自身的方法时,AOP通知会失效
- 正确的做法是借助 AopContext 取到当前代理对象并强转,之后调用,这样 AOP 通知依然会执行
- 代理对象在后置处理器还没初始化的时候,提前创建了,则 AOP 通知不会织入
- 由于 AOP 是借助 BeanPostProcessor 实现的,如果 BeanPostProcessor 还没初始化好,目标对象就已经创建了,则不可能在生成代理对象