AOP原理

通过前两节的学习,我们了解并熟悉了如何使用 AOP,作为热爱编程的程序员,应主动了解底层原理,知道它是如何实现的。

AOP代理原理讲解

我们知道 xml 中,AOP 使用的是 ProxyFactoryBean,而且底层原理相同,因此我们从这里讲起。首先,看这个类,如下所示。

public class ProxyFactoryBean extends ProxyCreatorSupport implements FactoryBean<Object>, BeanClassLoaderAware, BeanFactoryAware

图5.4是这个类的继承关系图。

image 2024 03 31 16 57 28 745
Figure 1. 图5.4 ProxyFactoryBean的继承关系图

从图中可以看到 ProxyFactoryBean 类继承了 ProxyCreatorSupport 类。ProxyCreatorSupport 类比较重要,下面有创建工厂与获取代理工厂。我们来看 ProxyCreatorSupport 下的方法,如图5.5所示。

image 2024 03 31 16 58 10 918
Figure 2. 图5.5 ProxyCreatorSupport下的方法

在图中有两个方法,一个为 createAopProxy,另一个为 getAopProxyFactory。我们先来看看 getAopProxyFactory方法,如下所示。

private AopProxyFactory aopProxyFactory;
public ProxyCreatorSupport() {
   this.aopProxyFactory = new DefaultAopProxyFactory();
}

从程序中可以发现,AopProxyFactory 由 DefaultAopProxyFactory 来实现。这里有个方法是 createAopProxy,用来判断我们的代理是 JDK 还是 cglib,代码如下所示。

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException
{
if(!config.isOptimize()&&!config.isProxyTargetClass()&& !this.hasNoUserSuppliedProxyInterfaces(config)) {
      return new JdkDynamicAopProxy(config);
   } else {
      Class<?> targetClass = config.getTargetClass();
      if (targetClass == null) {
            throw new AopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation.");
      } else {
          return (AopProxy)(!targetClass.isInterface() && !Proxy. isProxyClass(targetClass) ? new ObjenesisCglibAopProxy(config) : new JdkDynamicAopProxy(config));
      }
   }
}

在上面的代码中,首先判断是否有接口,如果有接口,则直接 new JdkDynamicAopProxy。如果没有接口,就从 AdvisedSupport 中获取目标对象 Class,然后对这个对象进行判断。在这里 JDK 的代理是 JdkDynamicAopProxy,cglib 的代理是 ObjenesisCglibAopProxy。

那么如何获取代理?DefaultAopProxyFactory 中可以产生两种 AopProxy 接口,这时内部逻辑可以根据给定的类创建不同的代理 AopProxy,因为这时返回的是 AopProxy 接口。

那么这个接口是怎样的?代码如下所示。

package org.springframework.aop.framework;
import org.springframework.lang.Nullable;
public interface AopProxy {
   Object getProxy();
   Object getProxy(@Nullable ClassLoader var1);
}

ProxyCreatorSupport核心代理类

上文介绍 ProxyFactoryBean 类时主要讲了其内部的两个方法,将方向指到了 AOP 的代理上,下面就讲它的子类。通过 IDEA 可以看到其类的子类,如图5.6所示。

image 2024 03 31 17 01 08 887
Figure 3. 图5.6 ProxyCreatorSupport的子类

ProxyFactory

ProxyFactory 类也是用于创建 proxy 对象的工厂类,一般用于动态应用 AOP 时,编程式地使用 AOP 代理。在使用时,需要为它指定需要代理的目标对象,代理时需要 Advice、Advisor。我们通过一个使用 ProxyFactory 创建代理的实例,进行介绍说明,代码如下所示。

public void testProxyFactory() {
   MyService myService = new MyService();
   ProxyFactory proxyFactory = new ProxyFactory(myService);
   proxyFactory.addAdvice(new MethodBeforeAdvice() {
      @Override
       public void before(Method method, Object[] args, Object target) throws Throwable {
          System.out.println("执行目标方法调用之前的逻辑");
      }
   });
   MyService proxy = (MyService) proxyFactory.getProxy();
   proxy.add();
}

先看看 ProxyFactory 的方法,如图5.7所示。

image 2024 03 31 17 02 42 588
Figure 4. 图5.7 ProxyFactory的方法

在上面的代码中,使用的是构造函数指定代理的对象,也可以使用父类 AdvisedSupport 中的 setTarget 方法设置代理。

在上面程序中的关于 MethodBeforeAdvice 的使用方法,会在5.3.3小节中进行说明。

AspectJProxyFactory

AspectJProxyFactory 类主要是用于集成 AspectJ 与 Spring。在上面的代码中,可以看到 ProxyFactory 能够绑定 Advice 与 Advisor,非常方便。但当在代码中已有存在的切面,这时使用 ProxyFactory 就不够方便。不过 Spring Boot 提供了 AspectJProxyFactory 类,可以直接指定代理对象的切面。

这个类一般用于基于 AspectJ 风格的 AOP 代理对象。举例说明,首先我们需要一个存在的切面,代码如下所示。

package com.springBoot.aop.aspect;
@Aspect
@Component
public class LogAscpect {
      private Logger logger=LoggerFactory.getLogger(LogAscpect. class);
   @Pointcut("execution( * com.springBoot.aop.pojo.impl.MyLogPrint.doPrint(..))")
   public void pointCut(){
   }
   @Before("pointCut()")
   public void before(){
      System.out.println("before log");
   }
}

下面是使用 AspectJProxyFactory 类的代码。

public void aspectJProxyFactoryDemo() {
   MyService myService = new MyService();
   AspectJProxyFactory proxyFactory = new AspectJProxyFactory(myService);
   proxyFactory.addAspect(LogAscpect.class);
   proxyFactory.setProxyTargetClass(true);
   MyService proxy = proxyFactory.getProxy();
   proxy.add();
}

在上面的代码中可以看到,proxyFactory.addAspect 将已经存在的切面与代理绑定。

通知和通知器

在5.2节讲解了连接点,也讲解了切面与切点,这里认识一下通知和通知器。

通知(Advice)

首先,我们需要知道通知(Advice)在连接点做什么。其实通知只是一个标识接口,没有具体的方法与定义,代码如下所示。

package org.aopalliance.aop;
public interface Advice {
}

但是常用的通知都需要继承 Advice。例如,我们看 AfterReturningAdvice 接口,用于连接点执行完返回执行的通知,代码如下所示。

package org.springframework.aop;
public interface AfterReturningAdvice extends AfterAdvice {
   void afterReturning(@Nullable Object var1, Method var2, Object[] var3, @Nullable Object var4) throws Throwable;
}

上面的代码中,我们可以看到参数是返回值 var1、目标方法 var2、参数 var3、目标方法类 var4。在连接点执行之后,会在这个方法中执行切面逻辑。

通知器(Advisor)

我们了解了切点(Pointcut)、通知(Advice),只要将切点与通知结合就可以变成通知器(Advisor)。我们来看通知器的接口,代码如下所示。

package org.springframework.aop;
import org.aopalliance.aop.Advice;
public interface Advisor {
   Advice EMPTY_ADVICE = new Advice() {
   };
   Advice getAdvice();
   boolean isPerInstance();
}

再看子接口的代码。

package org.springframework.aop;
public interface PointcutAdvisor extends Advisor {
   Pointcut getPointcut();
}

这里包含了两个重要的方法:getPointcut 与 getAdvice。