[AOP] 2. AOP的两种实现-Spring AOP以及AspectJ

本文详细介绍了Spring AOP和AspectJ的实现原理,包括Spring AOP的代理实现、两种代理方式(动态代理和CGLIB代理)以及AspectJ的字节码操作。还对比了两者的优缺点,强调了在特定需求下选择合适实现的重要性。
摘要由CSDN通过智能技术生成

在接触Spring以及种类繁多的Java框架时,很多开发人员(至少包括我)都会觉得注解是个很奇妙的存在,为什么加上了@Transactional之后,方法会在一个事务的上下文中被执行呢?为什么加上了@Cacheable之后,方法的返回值会被记录到缓存中,从而让下次的重复调用能够直接利用缓存的结果呢?

随着对AOP的逐渐应用和了解,才明白注解只是一个表象,在幕后Spring AOP/AspectJ做了大量的工作才得以实现这些神奇的功能。

那么,本文就来聊一聊Spring AOP和AspectJ的那些事,它们究竟有什么魔力才让这一切成为现实。

Spring AOP

基于代理(Proxy)的AOP实现

首先,这是一种基于代理(Proxy)的实现方式。下面这张图很好地表达了这层关系:

这张图反映了参与到AOP过程中的几个关键组件(以@Before Advice为例):

  1. 调用者Beans - 即调用发起者,它只知道目标方法所在Bean,并不清楚代理以及Advice的存在
  2. 目标方法所在Bean - 被调用的目标方法
  3. 生成的代理 - 由Spring AOP为目标方法所在Bean生成的一个代理对象
  4. Advice - 切面的执行逻辑

它们之间的调用先后次序反映在上图的序号中:

  1. 调用者Bean尝试调用目标方法,但是被生成的代理截了胡
  2. 代理根据Advice的种类(本例中是@Before Advice),对Advice首先进行调用
  3. 代理调用目标方法
  4. 返回调用结果给调用者Bean(由代理返回,没有体现在图中)

为了理解清楚这张图的意思和代理在中间扮演的角色,不妨看看下面的代码:

@Component
public class SampleBean {
   

  public void advicedMethod() {

  }

  public void invokeAdvicedMethod() {
    advicedMethod();
  }

}

@Aspect
@Component
public class SampleAspect {
   

  @Before("execution(void advicedMethod())")
  public void logException() {
    System.out.println("Aspect被调用了");
  }

}

sampleBean.invokeAdvicedMethod(); // 会打印出 "Aspect被调用了" 吗?

SampleBean扮演的就是目标方法所在Bean的角色,而SampleAspect扮演的则是Advice的角色。很显然,被AOP修饰过的方法是advicedMethod(),而非invokeAdvicedMethod()。然而,invokeAdvicedMethod()方法在内部调用了advicedMethod()。那么会打印出来Advice中的输出吗?

答案是不会

如果想不通为什么会这样,不妨再去仔细看看上面的示意图。

这是在使用Spring AOP的时候可能会遇到的一个问题。类似这种间接调用不会触发Advice的原因在于调用发生在目标方法所在Bean的内部,和外面的代理对象可是没有半毛钱的关系哦。我们可以把这个代理想象成一个中介,只有它知道Advice的存在,调用者Bean和目标方法所在Bean知道彼此的存在,但是对于代理或者是Advice却是一无所知的。因此,没有通过代理的调用是绝无可能触发Advice的逻辑的。如下图所示:

Spring AOP的两种实现方式

Spring AOP有两种实现方式:

  • 基于接口的动态代理(Dynamic Proxy)
  • 基于子类化的CGLIB代理

我们在使用Spring AOP的时候,一般是不需要选择具体的实现方式的。Spring AOP能根据上下文环境帮助我们选择一种合适的。那么是不是每次都能够这么”智能”地选择出来呢?也不尽然,下面的例子就反映了这个问题:

@Component
public class SampleBean implements SampleInterface {
   

  public void advicedMethod() {

  }

  public void invokeAdvicedMethod() {
    advicedMethod();
  }

}

public interface SampleInterface {
   }

在上述代码中,我们为原来的Bean实现了一个新的接口SampleI

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值