面向切面编程(Aspect-Oriented Programming,AOP)是一种编程范式,旨在提高代码的模块化程度,主要通过分离跨切关注点(cross-cutting concerns)来实现。下面是AOP中的一些重要概念:
简单点说,AOP 就是把一些业务逻辑中的相同代码抽取到一个独立的模块中,让业务逻辑更加清爽。
可以把日志记录和数据校验这两个功能抽取出来,形成一个切面,然后在业务代码中引入这个切面,这样就可以实现业务逻辑和通用逻辑的分离。
AOP 有哪些核心概念?
- 切面(Aspect):类是对物体特征的抽象,切面就是对横切关注点的抽象
- 连接点(Join Point):被拦截到的点,因为 Spring 只支持方法类型的连接点,所以在 Spring 中,连接点指的是被拦截到的方法,实际上连接点还可以是字段或者构造方法
- 切点(Pointcut):对连接点进行拦截的定位
- 通知(Advice):指拦截到连接点之后要执行的代码,也可以称作增强
- 目标对象 (Target):代理的目标对象
- 引介(introduction):一种特殊的增强,可以动态地为类添加一些属性和方法
- 织入(Weabing):织入是将增强添加到目标类的具体连接点上的过程。可以分为 3 种类型的织入:
①、编译期织入:切面在目标类编译时被织入。
②、类加载期织入:切面在目标类加载到 JVM 时被织入。需要特殊的类加载器,它可以在目标类被引入应用之前增强该目标类的字节码。
③、运行期织入:切面在应用运行的某个时刻被织入。一般情况下,在织入切面时,AOP 容器会为目标对象动态地创建一个代理对象。Spring AOP 就是以这种方式织入切面。
Spring 采用运行期织入,而 AspectJ 采用编译期织入和类加载器织入。
AOP的实现可以分为两类:静态织入和动态织入。这两类实现方式主要区别在于织入切面的时间点不同。
AOP 有哪些环绕方式?
AOP 一般有 5 种环绕方式:
- 前置通知 (@Before)
- 返回通知 (@AfterReturning)
- 异常通知 (@AfterThrowing)
- 后置通知 (@After)
- 环绕通知 (@Around)
Spring 的 AOP 是通过动态代理来实现的,动态代理主要有两种方式:
JDK 动态代理和 CGLIB 代理。
①、JDK 动态代理是基于接口的代理,只能代理实现了接口的类。使用 JDK 动态代理时,Spring AOP 会创建一个代理对象,该代理对象实现了目标对象所实现的接口,并在方法调用前后插入横切逻辑。
优点:只需依赖 JDK 自带的 java.lang.reflect.Proxy 类,不需要额外的库;
缺点:只能代理接口,不能代理类本身。
②、CGLIB 动态代理是基于继承的代理,可以代理没有实现接口的类。使用 CGLIB 动态代理时,Spring AOP 会生成目标类的子类,并在方法调用前后插入横切逻辑。
优点:可以代理没有实现接口的类,灵活性更高;缺点:需要依赖 CGLIB 库,创建代理对象的开销相对较大。
选择 CGLIB 还是 JDK 动态代理?
如果目标对象没有实现任何接口,则只能使用 CGLIB 代理。如果目标对象实现了接口,通常首选 JDK 动态代理。
虽然 CGLIB 在代理类的生成过程中可能消耗更多资源,但在运行时具有较高的性能。对于性能敏感且代理对象创建频率不高的场景,可以考虑使用 CGLIB。
JDK 动态代理是 Java 原生支持的,不需要额外引入库。而 CGLIB 需要将 CGLIB 库作为依赖加入项目中。