kun-song / my-blog

个人博客
3 stars 0 forks source link

AOP #41

Open kun-song opened 6 years ago

kun-song commented 6 years ago

AOP,即面向切面编程,是 Spring 的两大基石功能之一(另一个是 DI 依赖注入,或控制反转)。

学习 AOP,有几个问题需要搞懂:AOP 是什么、为什么需要 AOP、如何实现 AOP?

AOP 是什么

我理解的 AOP:将特定代码片段,插入指定位置,从而实现功能增强。

为什么需要 AOP

AOP 实际是对 OO 的强力补充,既然是补充,那就说明传统的 OO 肯定存在某种问题。

OO 的核心概念是继承、封装与多态,其中:

其中,封装要求我们将业务逻辑分割为不同的、互相独立的类,每个类有自己独特的责任,这非常完美,每个类各司其责、互相独立,逻辑非常清晰,而且有助于代码复用。

但这种做法的前提是:应用 的确 可以分割为互相独立的逻辑单元,遗憾的是,这个假设本身就不成立,因为实际的应用中,不同类之间总会共享某些功能,例如日志、安全、事务等,最后导致不同类都在重复实现相同功能,反而不美。

AOP 解决的就是上面的问题,通过将共享代码片段,动态插入指定位置,可以去除业务类中的冗余代码,将共享代码集中维护,还能降低维护工作量。

如何实现 AOP

AOP 是一种通用思想,实现则与具体框架有关系,在 Java 世界中,常见的有 Spring AOP 和 AspectJ 两种框架。

使用 Spring 实现 AOP 可以参考其 官方文档,其中也包含对 AOP 概念的讲解,推荐。

kun-song commented 6 years ago

OOP 和 AOP 都提升了模块化,两者相互补充,OOP 的模块化单元是 class,而 AOP 的模块化单元是 aspect。

kun-song commented 6 years ago

Spring 两大基本功能:IOC 和 AOP,两者互相独立,无依赖关系。

Spring 中的 AOP 主要有两个作用:

  1. 提供声明式的企业级(应用所需的)服务,其中最重要的是声明式的事务管理;
  2. 允许用户自定义 aspect,实现 OOP 与 AOP 的互相补充;

Spring AOP 遵循 AOP 的概念和术语,没有另起炉灶,虽然 AOP 概念本身就不太直观。

Spring AOP 仅支持方法级的连接点,不支持字段级的连接点(AspectJ 支持),Spring AOP 更注重 AOP 与 IoC 容器的紧密集成,并非要提供完整的 AOP 实现。

在 Spring 中,AOP 一般与 IoC 一起使用,例如 Aspect 就可以定义为普通的 bean,可以随意注入使用,有些功能使用 Spring AOP 难以实现,这是很正常的,遇到这种问题,可以使用 AspectJ 而非 Spring AOP。

Spring AOP 默认实现为 JDK 标准动态代理,它可为任意接口实现代理,但是当遇到类是,JDK 内置的动态代理就无能为力了,此时 Spring AOP 将自动切换到 CGLIB 实现。

kun-song commented 6 years ago

Spring AOP 切面

Aspect = Pointcut + Advice

在 Spring AOP 中,切面(Aspect)即用 @AspectJ 修饰的类,而 Spring AOP 仅支持方法级切点,所以切点就是方法,而 Advice 一般也是方法。

首先,需要开启 AspectJ 支持,参考 官方文档

切面的声明非常简单,使用 @Aspect 修饰的类即为切面:

@Aspect
public class NotVeryUsefulAspect {

}

切点决定了在何处织入 Advice,使用 @Pointcut(...) 修饰的方法即为切点,其声明包含两部分:

@Pointcut("execution(* transfer(..))")// the pointcut expression
private void anyOldTransfer() {}// the pointcut signature

Spring AOP 4.x.x 版本仅支持如下 AspectJ 切点指示符,其他的暂不支持:

注意,切点表达式可以用 && ||! 进行组合:

@Pointcut("execution(public * *(..))")
private void anyPublicOperation() {}

@Pointcut("within(com.xyz.someapp.trading..*)")
private void inTrading() {}

若想匹配 com.xyz.someapp.trading 包中的 public 方法可以用 && 组合:

@Pointcut("anyPublicOperation() && inTrading()")
private void tradingOperation() {}