面试官1分钟前
Spring AOP了解吗?
AOP有什么作用?
JDK和CGLIB动态代理有什么区别?
……
目录
- AOP基本概念
- 性质
- 目的
- 原理
- AspectJ静态代理
- Spring AOP动态代理
- AOP关键词
- 动态代理的两种实现
- JDK动态代理
- CGLIB动态代理
- 其他一些核心问题
AOP基本概念
AOP:Aspect Oriented Programming 也即面向切面编程
下面分三个方面来陈述:
- 性质:AOP本质上是一个什么东西。
- 目的:AOP之所以存在,是为了解决什么问题?
- 原理:AOP背后是如何实现的?
性质
AOP是一种设计思想。
就像OOP是面向对象编程一样,AOP则是OOP的一种延伸,是针对切面进行编程。
那么什么叫切面呢?可以认为是一个可重用的模块,或是一系列公共行为和逻辑。
如果具体举例子来说,比如权限认证(用户在进入任何功能性接口之前都应该先完成鉴权)、日志打印(许多接口出现问题的时候都应该有相应的异常日志)等等。
目的
借助AOP,我们希望:
- 通过尽可能地复用代码,以降低编码复杂度
- 通过把切面提取出来,以降低模块间的耦合度
- 联系上面这两条,同时也就提升了系统的可维护性
原理
AOP实现的关键在于代理模式
AOP代理主要分为静态代理和动态代理
静态代理的代表为AspectJ
动态代理的代表为Spring AOP
AspectJ静态代理
AspectJ会在编译阶段生成AOP代理类,因此也称为编译时增强。
它会在编译阶段将Aspect(切面)织入到Java字节码中,运行的时候利用到的就是增强之后的AOP对象。
Spring AOP动态代理
Spring AOP不会像AspectJ那样在编译时增强,而是每次在运行时于内存中临时为方法生成一个AOP对象。
这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。
如果理解了动态代理,那么对Spring AOP动态代理也不会难以理解。希望对动态代理不是很熟悉的读者可以先自行查阅相关资料学习。
AOP关键词
在Java AOP的世界里,有一些关键词是一定要明白的。
Aspect 切面
Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。
Joint point 连接点
表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。
Pointcut 切点
表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。
Advice 增强
Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。
Advice执行顺序(很重要!!)
环绕(@Around)开始阶段 → @Before → 执行切点方法 → 环绕结束阶段 → @After → @AfterReturning
Target 目标对象
织入Advice的目标对象。
Weaving 织入
将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程。
动态代理的两种实现
在上文的阅读中,我们知道Spring AOP是基于动态代理实现的。
它背后其实有两种实现方式:JDK动态代理和CGLIB动态代理。
JDK动态代理
利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类,
在调用具体方法前调用InvokeHandler来处理。
CGLIB动态代理
利用ASM开源包,修改代理对象类的class文件的字节码,生成子类,再加载class文件。
其他一些核心问题
1、何时使用JDK还是CGLIB?
- 如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP。
- 如果目标对象实现了接口,可以强制使用CGLIB实现AOP。
- 如果目标对象没有实现了接口,必须采用CGLIB库,Spring会自动在JDK动态代理和CGLIB之间转换。
2、JDK动态代理和CGLIB字节码生成的区别?
- JDK动态代理只能对实现了接口的类生成代理,而不能针对类。
- CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,
并覆盖其中方法实现增强,但是因为采用的是继承,所以该类或方法最好不要声明成final,
对于final类或方法,是无法继承的。private也是一样。
3、CGlib比JDK快?
- 使用CGLib实现动态代理,CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,在jdk6之前比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。
- 在jdk6、jdk7、jdk8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLIB代理效率,只有当进行大量调用的时候,jdk6和jdk7比CGLIB代理效率低一点,但是到jdk8的时候,jdk代理效率高于CGLIB代理,总之,每一次jdk版本升级,jdk代理效率都得到提升,而CGLIB代理消息确有点跟不上步伐。
4、Spring如何选择用JDK还是CGLIB?
- 当Bean实现接口时,Spring就会用JDK的动态代理。
- 当Bean没有实现接口时,Spring使用CGlib实现。
- 可以强制使用CGlib(在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>)。
总结
本篇中我们总结了Spring框架的AOP部分,
分别分析了AOP的概念、技术关键词,
及背后原理实现。