Spring中的AOP

Spring中的AOP

一.Spring AOP的概念

1.AOP的概述

AOP的全称是Aspect Oriented Programming,即面向切面编程。是通过预编译方式和运行期间动态代理,实现程序功能的统一维护的一种技术。AOP是OOP面向对象编程的一种延续。

使用OOP编程时,虽然我们可以通过继承关系实现大量代码重用,但是一些不具有继承关系的对象,也可能具有一些公共行为,比如日志记录等等。

面对这种没有继承关系而重复的代码,OOP的编程方式就不适合把它们重用和管理起来。

而AOP的出现,就为处理这类问题提供了一套完整的理论和灵活多样的解决方案。AOP可以将这些重复的代码抽取出来单独维护,而在需要使用时再统一调用。

OOP由于继承关系是纵向的,而AOP所关注的方向是横向的。AOP是对OOP一种很好的补充。

2.JoinPoint连接点

AOP的功能模块都是需要横向织入到各个纵向继承体系的OOP的模块中。要进行这种织入操作,我们就需要知道要在OOP模块的哪些执行点上进行织入操作。

这些将要在其之上进行织入操作的系统执行点就是JoinPoint连接点。这些连接点就是AOP的切入点。

在Spring中只支持方法类型的连接点。

2.1类图:

3.Advice通知

Advice通知是定义在JoinPoint连接点上的横切逻辑。在Spring AOP中,Advice是一个定义的接口。在这个接口的基础上,Spring提供了更具体的通知类型,如BeforeAdvice、AfterAdvice、ThrowsAdvice等。具体切面的增强,就是通过这些接口集成到AOP框架中去发挥作用的。

3.1 类图

3.2 BeforeAdvice

BeforeAdvice所实现的横切逻辑将在相应的Joinpoint之前执行,在BeforeAdvice执行完成之后,程序执行流程将从Joinpoint处继续执行,所以BeforeAdvice通常不会打断程序的执行流程。但是如果必要,也可以通过抛出相应异常的形式中断程序流程。

在Spring中,我们通过实现MethodBeforeAdvice接口,来实现BeforeAdvice的功能。

java 复制代码
public interface MethodBeforeAdvice extends BeforeAdvice {
   void before(Method method, Object[] args, @Nullable Object target) throws Throwable;
}

MethodBeforeAdvice接口中,定义了一个before的回调函数。Before回调函数中需要三个参数。

  • 目标方法反射的Method对象。
  • 目标方法的输入Object[]对象参数数组。
  • Object类型的目标对象

3.3 AfterAdvice

顾名思义,AfterAdvice所实现的横切逻辑将在相应的Joinpoint方法之后执行,但是方法是在正常返回的情况下。

在Spring中的定义了AfterReturningAdvice接口,接口中定义了afterReturning方法。通过该方法我们可以访问当前Joinpoint的方法返回值、方法、方法参数以及所在的目标对象。

java 复制代码
public interface AfterReturningAdvice extends AfterAdvice {

   void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable;

}

3.4 ThrowsAdvice

ThrowsAdvice所实现的横切逻辑将在相应的Joinpoint方法抛出异常后执行。通过ThrowsAdvice我们可以对系统中异常情况进行监控,以统一的方式对所发生的异常进行处理。

在Spring中的定义了ThrowsAdvice接口,该接口继承自AfterAdvice接口。ThrowsAdvice接口虽然没有定义任何方法,它在抛出异常时被回调,这个回调是AOP使用反射机制来完成的。

我们在实现相应的ThrowsAdvice的时候,需要遵循如下规则(以CountingThrowsAdvice为例)

java 复制代码
public static class CountingThrowsAdvice extends MethodCounter implements ThrowsAdvice {

   public void afterThrowing(IOException ex) throws Throwable {

      count(IOException.class.getName());

   }

   public void afterThrowing(UncheckedException ex) throws Throwable {

      count(UncheckedException.class.getName());

   }

}

@SuppressWarnings("serial")
static class UncheckedException extends RuntimeException {

}

4.Pointcut切点

Pointcut(切点)是决定Advice通知应该作用于哪些连接点(JoinPoint)。我们可以通过Pointcut来定义一些需要增强的方法的集合,这些集合的选取可以按照一定的规则来完成。

这些规则可以是根据某个方法名进行匹配,或者由某个正则表达式进行标识等。

4.1类图

4.2 Pointcut常用类

  • Pointcut接口
java 复制代码
public interface Pointcut {
   /**

    * Return the ClassFilter for this pointcut.

    * @return the ClassFilter (never {@code null})

    */

   ClassFilter getClassFilter();

   /**

    * Return the MethodMatcher for this pointcut.

    * @return the MethodMatcher (never {@code null})

    */

   MethodMatcher getMethodMatcher();

   /**

    * Canonical Pointcut instance that always matches.

    */

   Pointcut TRUE = TruePointcut.INSTANCE;
}

Pointcut接口中定义了两个方法getClassFilter(),getMethodMatcher(),分别返回ClassFilter和MethodMatcher类型的实例。

  • ClassFilter 是用于类型级别匹配被执行织入操作。
  • MethodMatcher是用于方法级别匹配被执行织入操作。

Pointcut接口还提供了一个TruePointcut的实例,来匹配所有的Joinpoint实例。

  • NameMatchMethodPointcut

NameMatchMethodPointcut继承自StaticMethodMatcherPointcut类,可以根据自身指定的一组方法名称与Joinpoint处的方法的方法名称进行匹配。

  • JdkRegexpMethodPointcut

JdkRegexpMethodPointcut也是StaticMethodMatcherPointcut类的子类,通过正则表达式与Joinpoint处的方法的方法名称进行匹配。

  • AnnotationMatchingPointcut

AnnotationMatchingPointcut直接实现了Pointcut接口,通过注解与Joinpoint处的方法的方法名称进行匹配。

  • ComposablePointcut

ComposablePointcut是Spring AOP提供的可以进行Pointcut逻辑运算的Pointcut实现。它可以进行Pointcut之间的"并"以及"交"运算。

5.Advisor通知器

完成对目标方法的切面增强设计(Advice)和切点的设计(Pointcut)以后,需要一个对象把它们结合起来,完成这个作用的就是Advisor(通知器)。Advisor是定义在哪个切点使用使用哪个通知,也就是说通过Advisor,把Advice和Pointcut结合起来,这个结合为使用IoC容器配置AOP应用,或者说即开即用地使用AOP基础设施,提供了便利。

5.1类图

二.Spring AOP的实现机制

在Spring AOP的实现中,使用的核心技术是动态代理和字节码生成技术。动态代理机制和字节码生成技术都是在运行期间为目标对象生成一个代理对象,再将横切逻辑织入到这个代理对象中,系统最终使用的是织入了横切逻辑的代理对象。

1.代理模式

1.1概念

代理模式给一个对象提供一个替身或占位符,来控制对原对象的访问。代理模式是一种对象结构型模式。

1.2类图

Subject (抽象主题 ***):***抽象主题类是真实主题类和代理类的共同接口。通过实现同一接口,代理类可以在真实主题类出现任何地方取代它。

RealSubject (真实主题 ***):***真实主题类是真正做事的对象,它实现了具体的业务逻辑操作。它是一个被代理类控制和访问的对象。

Proxy (代理类) ***:***代理类包含了对真实主题类的引用。由于它和真实主题类实现了相同的接口,所以在任何用到真实主题类的地方,都可以被代理类取代。代理类可以控制真实主题类的访问。客户端可以通过访问代理类,来间接调用真实主题类的操作。

1.3代码示例

java 复制代码
public interface ISubject {
   public void request();
}

public class RealSubject implements ISubject{
   @Override
   public void request() {
       System.out.println("do some request.");
   }
}

public class SubjectProxy implements ISubject{
   private ISubject iSubject;
   
   public SubjectProxy(ISubject iSubject){
      this.iSubject = iSubject;
   }

   @Override
   public void request() {
      //在调用真实主题类方法前,加入前置逻辑,对原方法进行增强。
      preProcess();
      this.iSubject.request();
      //在调用真实主题类方法后,加入后置逻辑,对原方法进行增强。
      postProcess();
   }

   private void preProcess() {
      System.out.println("do some pre-process business logic.");
   }   

   private void postProcess() {
      System.out.println("do some post-process business logic.");
   }   

   public ISubject getiSubject() {
      return iSubject;
   }

   public void setiSubject(ISubject iSubject) {
      this.iSubject = iSubject;
   }

}

public class App {
  public static void main(String[] args) {
     ISubject iSubject = new RealSubject();
     SubjectProxy subjectProxy = new SubjectProxy(iSubject);
     subjectProxy.request();
  }
}

在代码示例中,我们定义了一个抽象主题接口ISubject,接口中就定义了一个request()方法。真实主题类RealSubject和代理主题类SubjectProxy都实现了ISubject接口,RealSubject和SubjectProxy都对ISubject接口中定义的request()给出了实现。

代理主题类SubjectProxy定义了一个对ISubject类型实例的引用,这个引用ISubject类型实例一般都是真实主题类RealSubject的实例。

当我们调用代理主题类SubjectProxy的request()方法时,其实真正调用的是所引用的真实主题类RealSubject的request()方法。不过在调用RealSubject的request()方法前后,我们可以分别添加前置和后置的处理逻辑,这样就达到了对原方法增强的目的。

RealSubject是系统中的Joinpoint所在的目标对象,那么我们就可以为这个目标对象创建一个代理对象SubjectProxy,然后将横切逻辑(preProcess()和postProcess方法)添加到这个代理对象中。当系统使用这个代理对象运行的时候,那么原有逻辑的实现和横切逻辑就完全融合到这个代理对象中。

Spring AOP本质上就是采用这种代理机制的原理实现的。只不过这种静态代理的模式,对于相同的横切逻辑,需要为每一个不同的目标对象,都创建一个代理对象。

如果系统中存在成百上千的符合Pointcut匹配条件的目标对象时,那我们为这成百上千的目标对象创建成百上千的代理对象,明显不合实际情况。但是动态代理就可以很好解决这个问题。

2.动态代理

2.1概念

Java标准库中提供了一种动态代理(Dynamic Proxy)的机制。使用该机制,我们可以为指定的接口,在系统运行期间动态的生成代理对象。代理对象将请求转发给了实际的目标对象,并在转发过程中可以增加额外的操作。这样我们就可以在不修改源代码的情况下,对原有的类功能进行了增强。所以动态代理比静态代理更加灵活和易于扩展。

2.2动态代理的实现

在Java中动态代理主要通过Proxy类和InvocationHandler接口来实现的。

  • java.lang.reflect.Proxy类:Proxy类中定义了newProxyInstance()方法,专门用于创建代理对象。newProxyInstance()方法需要三个参数。
    1. ClassLoader:类加载器。
    2. Class<?>[]: 需要实现的接口数组,至少需要传入一个接口进去。
    3. InvocationHandler:InvocationHandler接口的实例。
  • InvocationHandler接口:InvocationHandler接口定义了一个invoke方法。代理对象在执行方法时都会调用invoke方法。invoke方法就是我们实现横切逻辑的地方,相当于AOP中的Advice通知。

InvocationHandler接口的实例,是作为参数传给了Proxy类的newProxyInstance()方法。

2.3代码示例

java 复制代码
public interface ISubject {
   public void request();
}

public class RealSubject implements ISubject{

   @Override
   public void request() {
      System.out.println("this is do request");
   }
}

public class RequestHandler implements InvocationHandler {

   private ISubject target;

   public RequestHandler(ISubject target) {
      this.target = target;
   }  
   
   @Override
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      if(method.getName().equals("request")) {
         System.out.println("dynamic proxy do some thing");
      }
      return method.invoke(target, args);
   }
}

public class Demo {
   
  public static void main(String[] args) {
      ISubject target = new RealSubject();     
      ISubject subject = (ISubject)Proxy.newProxyInstance(target.getClass().getClassLoader(), 
            new Class[] {ISubject.class},
            new RequestHandler(target));
    
      subject.request();
   }
}

在以上动态代理代码示例中,我们同静态代理一样定义了一个抽象主题接口ISubject和其实现类SubjectProxy。

和静态代理所不同的是,我们定义了一个RequestHandler类实现了InvocationHandler接口。在RequestHandler类中,我们现实了invoke()方法。在invoke()方法方法中,我们可以加入横切逻辑。

最后再使用Proxy类来动态生成代理对象。当程序调用代理对象的方法时,对应的InvocationHandler实例就会在invoke()方法中拦截相应的方法调用。在invoke()方法中并进行横切处理。

如果还有其它目标对象和SubjectProxy类织入的横切逻辑是相同的,那我们使用Proxy类和同一个RequestHandler类为它生成相应的动态代理实例即可。不需要像静态代理那样,需要为每一个不同的目标类都都定义一个不同的代理类。

动态代理为我们减少了很多重复的代码,但是动态代理机制只能对实现了相应接口的类来使用。

如果一个类没有实现任何接口,我们就需要使用动态字节码技术,来为目标对象生成动态的代理对象实例。

3.动态字节码技术

3.1概述

Spring AOP中使用的动态字节码生成技术是CGLIB。CGLIB的全称是:Code Generation Library。CGLIB是一个强大的、高性能、高质量的代码生成类库,它可以为没有实现接口的类提供代理,是JDK动态代理技术的一种良好补充。

CGLIB的原理是对目标类进行继承扩展,为其动态的生成相应的代理子类。在子类中通过覆写父类的方法来扩展父类的行为(父类的final方法除外),子类会采用方法拦截的技术拦截父类方法的调用,在拦截父类方法的过程中,可以顺势织入增强的横切逻辑。

3.2代码示例

我们定义一个目标类SubjectImpl,在这个类中声明了一个request方法,这个方法非常简单,就是打印出" this is do request"这段内容。

java 复制代码
public class SubjectImpl {

   public void request() {
      System.out.println("this is do request");
   }

}

我们再定义一个RequestHandler类,该类实现了MethodInterceptor接口。RequestHandler类覆写了MethodInterceptor接口的intercept方法。

这个intercept方法其实是CGLIB的回调函数。目标对象的任何方法执行时,都会被拦截,代码会被执行到intercept方法中。在intercept方法中,我们可以增加自定义的横切逻辑。

在本例的intercept方法中,会判断果目标对象执行的方法名是否是request时,如果是,就会增加一些自定义逻辑。intercept方法最后是再执行目标对象的方法。

java 复制代码
public class RequestHandler implements MethodInterceptor{

   @Override
   public Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) throws Throwable {
      if(method.getName().equals("request")) {
         System.out.println("cglib do some thing");
      }
      return proxy.invokeSuper(object, args);
   }
}

示例Demo:

java 复制代码
public class Demo {

   public static void main(String[] args) {
      //创建Enhancer对象
      Enhancer enhancer = new Enhancer();
      //Enhancer对象设置目标类的类型
      enhancer.setSuperclass(SubjectImpl.class);
      //Enhancer对象设置回调函数的类
      enhancer.setCallback(new RequestHandler());
      //创建代理对象
      SubjectImpl proxy = (SubjectImpl)enhancer.create();
      //代理对象执行目标对象的方法
      proxy.request();
   }
}

运行结果:

三.Spring AOP的设计与实现

1.概述

在Spring AOP中,使用的核心技术是动态代理。动态代理会在系统运行期间为目标对象动态的生成代理对象。这个代理对象的建立可以通过JDK的Proxy类来完成,也可以通过CGLIB来完成。

Spring AOP会在代理对象的拦截器中,织入各种横切逻辑。Spring AOP给出了一系列Advice横切实现,比如BeforeAdvice(前置通知)、AfterAdvice(后置通知)、ThrowsAdvice(异常通知等)异常通知等。Spring AOP还提供了一系列的Pointcut来匹配切入点。Spring AOP中还设计了一系列Adapter,各个Adapter会匹配Advice和其特定的拦截器,实现各种Advice织入。

2.AopProxy代理对象的创建

Spring中分别使用了JDK动态代理和CGLIB两种技术来生成代理对象。对于这两种代理对象生成的模式,Spring中使用了工厂模式。

类图:

  • AopProxy接口:AopProxy接口中定义两个获取代理对象的方法。AopProxy接口相当于工厂模式中的抽象产品类。
java 复制代码
public interface AopProxy {
   Object getProxy();
   Object getProxy(@Nullable ClassLoader classLoader);
}
  • JdkDynamicAopProxy类:JdkDynamicAopProxy类实现了AopProxy接口。JdkDynamicAopProxy类在覆写父类中的getProxy方法时,使用JDK动态代理的方式来生成一个代理对象。
java 复制代码
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {

   if (logger.isTraceEnabled()) {
      logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
   }

   Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
   findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
   return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

JdkDynamicAopProxy类还实现了InvocationHandler接口,覆写了该接口的invoke方法。代理对象在执行方法时都会调用InvocationHandler实例的invoke方法。在这个invoke方法中可以对目标对象的方法进行拦截,在拦截中我们可以织入横切逻辑。

在这个工厂模式中,JdkDynamicAopProxy相当于Jdk动态代理类型的一个具体的产品类。

  • CglibAopProxy类:CglibAopProxy类也实现了AopProxy接口。在CglibAopProxy类中的getProxy方法中,则使用了动态字节码生成技术CGLIB,来生产一个代理对象。
java 复制代码
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
   if (logger.isTraceEnabled()) {
      logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource());
   }

   try {
      Class<?> rootClass = this.advised.getTargetClass();
      Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");
      Class<?> proxySuperClass = rootClass;
      if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {
         proxySuperClass = rootClass.getSuperclass();
         Class<?>[] additionalInterfaces = rootClass.getInterfaces();
         for (Class<?> additionalInterface : additionalInterfaces) {
            this.advised.addInterface(additionalInterface);
         }
      }

      // Validate the class, writing log messages as necessary.
      validateClassIfNecessary(proxySuperClass, classLoader);
      // Configure CGLIB Enhancer...
      Enhancer enhancer = createEnhancer();
      if (classLoader != null) {
         enhancer.setClassLoader(classLoader);
         if (classLoader instanceof SmartClassLoader &&
               ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
            enhancer.setUseCache(false);
         }
      }

      enhancer.setSuperclass(proxySuperClass);
      enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
      enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
      enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));

      Callback[] callbacks = getCallbacks(rootClass);
      Class<?>[] types = new Class<?>[callbacks.length];
      for (int x = 0; x < types.length; x++) {
         types[x] = callbacks[x].getClass();
      }

      // fixedInterceptorMap only populated at this point, after getCallbacks call above
      enhancer.setCallbackFilter(new ProxyCallbackFilter(
            this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
      enhancer.setCallbackTypes(types);

      // Generate the proxy class and create a proxy instance.
      return createProxyClassAndInstance(enhancer, callbacks);
   }
   catch (CodeGenerationException | IllegalArgumentException ex) {
      throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
            ": Common causes of this problem include using a final class or a non-visible class",
            ex);
   }
   catch (Throwable ex) {
      // TargetSource.getTarget() failed
      throw new AopConfigException("Unexpected AOP exception", ex);
   }
}

由此可见,CglibAopProxy类也是AopProxy抽象产品类的另一种形式的具体产品类。

  • AopProxyFactory接口:光有定义了产品定义还是不行的,我们还得有工厂来对定义的产品进行生产。

Spring中定义了AopProxyFactory接口,来规范AopProxy类型的实例的创建。AopProxyFactory接口就是一个抽象的工厂类。

java 复制代码
public interface AopProxyFactory {
   AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException;
}
  • DefaultAopProxyFactory类:DefaultAopProxyFactory类是对AopProxyFactory接口的一个默认实现。DefaultAopProxyFactory是一个具体工厂类。

在DefaultAopProxyFactory类的createAopProxy方法中,会进行一些逻辑判断,来决定是实例化一个JdkDynamicAopProxy对象,还是实例化一个CglibAopProxy对象。

Spring是使用JDK动态代理还是CGLIB来创建代理对象,受下面的一些条件是影响:

  • optimize:是否使用激进的优化策略, 来控制通过CGLIB创建代理对象。
  • proxyTargetClass:这个属性值为true时,表示目标类本身被代理而不是目标类的接口。
  • hasNoUserSuppliedProxyInterfaces:是否存在代理接口。
java 复制代码
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
   if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
      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.");
      }

      if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
         return new JdkDynamicAopProxy(config);
      }
      return new ObjenesisCglibAopProxy(config);
   }
   else {
      return new JdkDynamicAopProxy(config);
   }
}

3.Spring AOP拦截器的实现

在Spring AOP中可以通过JDK的Proxy方式或者CGLIB方式来生成代理对象。在代理对象生成时,我们可以通过bean配置文件或者手动写代码来为生成的代理对象增加一系列的拦截器。当我们调用目标对象的方法时,都会触发代理对象的回调方法,在回调方法中就会执行一系列的拦截器的方法。通过这些拦截器,我们就可以把一些逻辑横向织入到代码,已达到实现AOP的目的。

  • JDK的动态代理

JDK的动态代理中,InvocationHandler接口定义了invoke方法,这个invoke方法是作为JDK Proxy动态代理对象进行拦截的回调入口。

在Spring AOP的JDK动态代理实现方式中,是JdkDynamicAopProxy类中实现了InvocationHandler接口。也就是说当Proxy对象的代理方法被调用时,JdkDynamicAopProxy类中的invoke方法作为回调函数被触发,从而通过invoke的具体实现,来完成对目标对象方法调用的拦截或者说是功能增强的工作

JdkDynamicAopProxy类中的invoke方法源码

java 复制代码
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

   Object oldProxy = null;
   boolean setProxyContext = false;
   TargetSource targetSource = this.advised.targetSource;
   Object target = null;

   try {
      if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
         // The target does not implement the equals(Object) method itself.
         return equals(args[0]);
      }
      else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
         // The target does not implement the hashCode() method itself.
         return hashCode();
      }
      else if (method.getDeclaringClass() == DecoratingProxy.class) {
         // There is only getDecoratedClass() declared -> dispatch to proxy config.
         return AopProxyUtils.ultimateTargetClass(this.advised);
      }
      else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
            method.getDeclaringClass().isAssignableFrom(Advised.class)) {
         // Service invocations on ProxyConfig with the proxy config...
         return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
      }

      Object retVal;
      if (this.advised.exposeProxy) {
         // Make invocation available if necessary.
         oldProxy = AopContext.setCurrentProxy(proxy);
         setProxyContext = true;
      }

      // Get as late as possible to minimize the time we "own" the target,
      // in case it comes from a pool.
      //获得目标对象
      target = targetSource.getTarget();
      Class<?> targetClass = (target != null ? target.getClass() : null);
      // Get the interception chain for this method.
      //获得定义好的拦截器链
      List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
      
      // Check whether we have any advice. If we don't, we can fallback on direct
      // reflective invocation of the target, and avoid creating a MethodInvocation.
      //判断拦截器链是否为空
      if (chain.isEmpty()) {
         // We can skip creating a MethodInvocation: just invoke the target directly
         // Note that the final invoker must be an InvokerInterceptor so we know it does
         // nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
         Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
         //如果拦截器链为空,则直接调用目标对象的方法
         retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
      }
      else {
         // We need to create a method invocation...
         //如果拦截器链不为空,则构建一个ReflectiveMethodInvocation对象
         MethodInvocation invocation =
               new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
         // Proceed to the joinpoint through the interceptor chain.
         //ReflectiveMethodInvocation对象则会按按顺序一个个调用拦截器,
         // 完成拦截器的调用后,最后调用目标对象的方法
         retVal = invocation.proceed();
      }

      // Massage return value if necessary.
      Class<?> returnType = method.getReturnType();
      if (retVal != null && retVal == target &&
            returnType != Object.class && returnType.isInstance(proxy) &&
            !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
         // Special case: it returned "this" and the return type of the method
         // is type-compatible. Note that we can't help if the target sets
         // a reference to itself in another returned object.
         retVal = proxy;
      }
      else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
         throw new AopInvocationException(
               "Null return value from advice does not match primitive return type for: " + method);
      }
      return retVal;
   }
   finally {
      if (target != null && !targetSource.isStatic()) {
         // Must have come from TargetSource.
         targetSource.releaseTarget(target);
      }
      if (setProxyContext) {
         // Restore old proxy.
         AopContext.setCurrentProxy(oldProxy);
      }
   }
}
  • CGLIB动态字节码技术

对于动态字节码技术,在Spring AOP中我们是通过CglibAopProxy对象的getProxy方法来获取动态代理对象。

在CglibAopProxy对象的getProxy方法中,会创建Enhancer对象,最终是通过Enhancer对象来生成代理对象。Spring AOP在生成代理对象的同时,也会设置回调函数。这个回调函数是在DynamicAdvisedInterceptor对象中实现的。同样的当代理对象的代理方法被调用时,DynamicAdvisedInterceptor对象中的intercept方法也会作为回调函数被触发。

DynamicAdvisedInterceptor类中的intercept方法源码

java 复制代码
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

   Object oldProxy = null;
   boolean setProxyContext = false;
   Object target = null;
   TargetSource targetSource = this.advised.getTargetSource();
   try {
      if (this.advised.exposeProxy) {
         // Make invocation available if necessary.
         oldProxy = AopContext.setCurrentProxy(proxy);
         setProxyContext = true;
      }
      // Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...
      //获得目标对象
      target = targetSource.getTarget();
      Class<?> targetClass = (target != null ? target.getClass() : null);
      //获得定义好的拦截器链
      List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
      
      Object retVal;
      // Check whether we only have one InvokerInterceptor: that is,
      // no real advice, but just reflective invocation of the target.
      //判断拦截器链是否为空
      if (chain.isEmpty() && CglibMethodInvocation.isMethodProxyCompatible(method)) {
         // We can skip creating a MethodInvocation: just invoke the target directly.
         // Note that the final invoker must be an InvokerInterceptor, so we know
         // it does nothing but a reflective operation on the target, and no hot
         // swapping or fancy proxying.
         Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
         try {
            //如果拦截器链为空,则直接调用目标对象的方法
            retVal = methodProxy.invoke(target, argsToUse);
         }
         catch (CodeGenerationException ex) {
            CglibMethodInvocation.logFastClassGenerationFailure(method);
            retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
         }
      }
      else {
         // We need to create a method invocation...
         //如果拦截器链不为空,则构建一个CglibMethodInvocation对象
         //在proceed方法完成拦截器的调用后,最后调用目标对象的方法
         retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
      }
      retVal = processReturnType(proxy, target, method, retVal);
      return retVal;
   }
   finally {
      if (target != null && !targetSource.isStatic()) {
         targetSource.releaseTarget(target);
      }
      if (setProxyContext) {
         // Restore old proxy.
         AopContext.setCurrentProxy(oldProxy);
      }
   }
}

通过比对JdkDynamicAopProxy类中的invoke方法和DynamicAdvisedInterceptor类的intercept方法,我们就会发现两个方法做的事情大致是一样的。其实两个方法都主要做了两件事情。

  1. 获得定义好的拦截器链。

2.构建一个ReflectiveMethodInvocation对象,再调用ReflectiveMethodInvocation对象的proceed方法,在proceed方法中完成完成拦截器的调用和目标对象方法的调用。虽然DynamicAdvisedInterceptor类中创建的是CglibMethodInvocation对象,但CglibMethodInvocation其实是ReflectiveMethodInvocation的子类。

3.1拦截器链的获取

对于拦截器链的获取,在JdkDynamicAopProxy类中的invoke方法和DynamicAdvisedInterceptor类的intercept方法中都有一行这样的代码。

java 复制代码
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

从以上的代码中可以看出,是通过AdvisedSupport对象的getInterceptorsAndDynamicIntercep

-tionAdvice方法来获取拦截器链的。

getInterceptorsAndDynamicInterceptionAdvice方法源码

java 复制代码
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {

   //获取拦截器链使用了缓存,以提高效率。
   MethodCacheKey cacheKey = new MethodCacheKey(method);
   //首先从缓存中获取拦截连
   List<Object> cached = this.methodCache.get(cacheKey);
   //如果缓存中没有的话,则需要重新生成。缓存中有点话,则直接返回。
   if (cached == null) {
      cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
            this, method, targetClass);
      this.methodCache.put(cacheKey, cached);
   }
   return cached;
}

从以上的代码中可以看出,真正获取拦截链是调用了AdvisorChainFactory接口的getInterceptorsAndDynamicInterceptionAdvice方法。AdvisorChainFactory接口是一个生成拦截器链的抽象工厂类,该接口有一个默认实现的具体工厂类DefaultAdvisorChainFactory。

java 复制代码
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
      Advised config, Method method, @Nullable Class<?> targetClass) {

   // This is somewhat tricky... We have to process introductions first,
   // but we need to preserve order in the ultimate list.
   //获取一个AdvisorAdapterRegistry实例
   AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
   //获得通知器链,这个通知器链可以在bean文件中配置,也可以通过代码手动添加。
   Advisor[] advisors = config.getAdvisors();
   List<Object> interceptorList = new ArrayList<>(advisors.length);
   Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
   
   Boolean hasIntroductions = null;
   //遍历通知器链
   for (Advisor advisor : advisors) {
      //如果通知器是PointcutAdvisor类型的
      if (advisor instanceof PointcutAdvisor) {
         // Add it conditionally.
         PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
         //判断是否已经进行了预筛选的匹配操作或者进行类型级别匹配织入操作
         if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
            //进行方法级别匹配被执行织入操作
            MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
            boolean match;
            if (mm instanceof IntroductionAwareMethodMatcher) {
               if (hasIntroductions == null) {
                  hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
               }
               match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
            }
            else {
               match = mm.matches(method, actualClass);
            }

            //如果有匹配的通知器
            if (match) {
               //通过AdvisorAdapterRegistry实例,来获取通知器的拦截器链
               MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
               if (mm.isRuntime()) {
                  // Creating a new object instance in the getInterceptors() method
                  // isn't a problem as we normally cache created chains.
                  for (MethodInterceptor interceptor : interceptors) {
                     interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
                  }
               }
               else {
                  interceptorList.addAll(Arrays.asList(interceptors));
               }
            }
         }
      }
      //如果通知器是IntroductionAdvisor类型的
      else if (advisor instanceof IntroductionAdvisor) {
         IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
         //判断是否已经进行了预筛选的匹配操作或者进行类型级别匹配织入操作
         if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
            //通过AdvisorAdapterRegistry实例,来获取通知器的拦截器链
            Interceptor[] interceptors = registry.getInterceptors(advisor);
            interceptorList.addAll(Arrays.asList(interceptors));
         }
      }
      else {
         //通过AdvisorAdapterRegistry实例,来获取通知器的拦截器链
         Interceptor[] interceptors = registry.getInterceptors(advisor);
         interceptorList.addAll(Arrays.asList(interceptors));
      }
   }
   return interceptorList;
}

以上代码其实主要做了三件事情:

  1. 获得通知器链。
  2. 根据通知器的类型来进行类型和方法级别的匹配。
  3. 通过AdvisorAdapterRegistry实例来获取通知器上对应的拦截器。

3.1.1获得通知器链

Spring AOP中是通过调用了AdvisedSupport对象的getAdvisors方法来获取获得通知器链的。这个通知器链在获取代理对象前就已经完成了初始化。

在ProxyFactoryBean的getObject方法中会调用initializeAdvisorChain方法对通知器链进行了初始化。其实这些通知器Advisor是配置在bean的XML文件中。Spring IoC容器通过getBean方法把这些通知器转化为一个个bean,然后放入Advisor的链表中。

我们也可以通过手动调用AdvisedSupport对象的addAdvisor方法,把通知器Advisor加入到Advisor的链表中。

3.1.2类和方法级别的匹配

Advisor通知器是通知(Advice)和切点(Pointcut)的结合体。我可以从Advisor实例中获取Pointcut实例,Pointcut切点定义一些匹配规则来决定通知(Advice)是否能作用于JoinPoint连接点。这些匹配规则可以是类级别或者方法级别。

以上代码中PointcutAdvisor类型的通知器,需要先对目标类进行匹配,再对目标类的方法进行匹配。而IntroductionAdvisor类型的通知器,则只对目标类进行了匹配。

3.1.3获拦截器

Spring AOP中拦截器的获取其实是通过AdvisorAdapterRegistry实例的getInterceptors方法来实现的。所以在获取拦截器前,首先必须实例化一个AdvisorAdapterRegistry类型的对象。

java 复制代码
//获取一个AdvisorAdapterRegistry实例
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();

从以上代码可以看出AdvisorAdapterRegistry实例,是通过GlobalAdvisorAdapterRegistry类的getInstance这个静态方法获取的。GlobalAdvisorAdapterRegistry类是一个标准的单例模式的实现。

单例模式确保某一个类只有一个实例,只提供一个全局访问点,该类自行实例化并向整个系统提供这个实例,这个类就是单例类。

java 复制代码
public final class GlobalAdvisorAdapterRegistry {

   private GlobalAdvisorAdapterRegistry() {
   }

   private static AdvisorAdapterRegistry instance = new DefaultAdvisorAdapterRegistry();

   public static AdvisorAdapterRegistry getInstance() {
      return instance;
   }

   static void reset() {
      instance = new DefaultAdvisorAdapterRegistry();
   }
}

在GlobalAdvisorAdapterRegistry类中其构造函数是private类型的,所以该类不能使用new关键字来进行实例化。它定义了一个静态的final变量instance,这个AdvisorAdapterRegistry类型的instance是在类加载的时候就new了一个DefaultAdvisorAdapterRegistry类型的实例。DefaultAdvisorAdapterRegistry实例的获取是通过静态的getInstance()方法来直接返回instance变量。使用单例模式保证了DefaultAdvisorAdapterRegistry对象的唯一性。

DefaultAdvisorAdapterRegistry类是AdvisorAdapterRegistry接口的一个默认实现类。在DefaultAdvisorAdapterRegistry类中定义了一个AdvisorAdapter类型的列表adapters。在DefaultAdvisorAdapterRegistry类的构造函数中,会将MethodBeforeAdviceAdapter,AfterReturningAdviceAdapter,ThrowsAdviceAdapter这三个适配器注册到adapters的列表中。这三个适配器分别对应了MethodBeforeAdvice,AfterReturningAdvice,ThrowsAdvice三个不同的Advice通知,三个适配器都实现了AdvisorAdapter接口。

Spring AOP是通过在代理类中设置拦截器,来实现面向切面的编程。以上三个适配器的作用就是把通知转化为相应的拦截器。这三个适配器的编码使用了适配器模式。

适配器模式将一个接口转换成客户期望的另一个接口,使得原本由于接口不兼容的那些类可以一起工作。适配器模式是一种结构型模式。

我们以MethodBeforeAdviceAdapter类为例,来分析一下适配器模式在Spring AOP中的使用。其它的AfterReturningAdviceAdapter和ThrowsAdviceAdapter原理相同与MethodBeforeAdviceAdapter类相同。

AdvisorAdapter接口相当于适配器模式中目标抽象接口Target。它定义了两个方法。

java 复制代码
boolean supportsAdvice(Advice advice);

这个方法是用于判断Advice通知的类型。

java 复制代码
MethodInterceptor getInterceptor(Advisor advisor);

getInterceptor是根据Advisor通知器所包含的通知,来返回相应的拦截器。

MethodBeforeAdvice方法前置通知相当于适配者Adaptee,它是一个需要转换的实例,它会按照目标接口最后转换一个拦截器。

MethodBeforeAdviceAdapter是适配器类,主要是对目标抽象接口AdvisorAdapter和适配者类MethodBeforeAdvice进行了适配。MethodBeforeAdviceAdapter实现了AdvisorAdapter接口。

java 复制代码
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {

   @Override
   public boolean supportsAdvice(Advice advice) {
      return (advice instanceof MethodBeforeAdvice);
   }

   @Override
   public MethodInterceptor getInterceptor(Advisor advisor) {
      MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
      return new MethodBeforeAdviceInterceptor(advice);
   }
}

在MethodBeforeAdviceAdapter类的supportsAdvice方法中,会判断Advice通知是否为MethodBeforeAdvice类型。而在其getInterceptor方法中,则会在Advisor通知器中获取MethodBeforeAdvice类型的通知,最后将这个MethodBeforeAdvice类型的通知封装成一个MethodBeforeAdviceInterceptor类型的拦截器返回。

通过MethodBeforeAdviceAdapter这个适配器,将MethodBeforeAdvice这个通知转换成了MethodBeforeAdviceInterceptor的拦截器,并将这个拦截器返回。这样我们就可以通过MethodBeforeAdviceAdapter类的getInterceptor方法获取到拦截器了。

MethodBeforeAdviceInterceptor拦截器中定义了invoke方法,invoke方法中会执行MethodBeforeAdvice通知定义的横切逻辑。

java 复制代码
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice, Serializable {

   private final MethodBeforeAdvice advice;
   public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
      Assert.notNull(advice, "Advice must not be null");
      this.advice = advice;
   }

   @Override
   public Object invoke(MethodInvocation mi) throws Throwable {
      this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
      return mi.proceed();
   }
}

通过以上所有种种的铺垫,最后我们再来看看AdvisorAdapterRegistry实例的getInterceptors方法。

java 复制代码
public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable {

    //定义了一个AdvisorAdapter类型的List
   private final List<AdvisorAdapter> adapters = new ArrayList<>(3);

   //在构造函数中注册MethodBeforeAdviceAdapter,AfterReturningAdviceAdapter,ThrowsAdviceAdapter三个适配器。
   //这三个适配器分别对应了MethodBeforeAdvice,AfterReturningAdvice,ThrowsAdvice三个通知
   public DefaultAdvisorAdapterRegistry() {
      registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
      registerAdvisorAdapter(new AfterReturningAdviceAdapter());
      registerAdvisorAdapter(new ThrowsAdviceAdapter());
   }

   //将advice通知包装成Advisor通知器
   @Override
   public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
      if (adviceObject instanceof Advisor) {
         return (Advisor) adviceObject;
      }

      if (!(adviceObject instanceof Advice)) {
         throw new UnknownAdviceTypeException(adviceObject);
      }

      Advice advice = (Advice) adviceObject;
      if (advice instanceof MethodInterceptor) {
         // So well-known it doesn't even need an adapter.
         return new DefaultPointcutAdvisor(advice);
      }

      for (AdvisorAdapter adapter : this.adapters) {
         // Check that it is supported.
         if (adapter.supportsAdvice(advice)) {
            return new DefaultPointcutAdvisor(advice);
         }
      }
      throw new UnknownAdviceTypeException(advice);
   }


   //通过通知器来获取通知器所对应的拦截器数组
   @Override
   public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {

      List<MethodInterceptor> interceptors = new ArrayList<>(3);
      //从advisor通知器获取到对应的advicer通知
      Advice advice = advisor.getAdvice();
      //如果通知advice是MethodInterceptor类型的,则直接加入到interceptors的列表
      if (advice instanceof MethodInterceptor) {
         interceptors.add((MethodInterceptor) advice);
      }

      //循环所有已注册的adapter(MethodBeforeAdviceAdapter,AfterReturningAdviceAdapter,ThrowsAdviceAdapter)
      for (AdvisorAdapter adapter : this.adapters) {
            //如果这个advice通知是当前adapter所支持的类型
         if (adapter.supportsAdvice(advice)) {
            //对advisor通知器进行适配,从adapter中取出对应封装好AOP编织功能的拦截器
            interceptors.add(adapter.getInterceptor(advisor));
         }
      }

      if (interceptors.isEmpty()) {
         throw new UnknownAdviceTypeException(advisor.getAdvice());
      }
      return interceptors.toArray(new MethodInterceptor[0]);
   }



   //把适配器注册到AdvisorAdapter类型的List中
   @Override
   public void registerAdvisorAdapter(AdvisorAdapter adapter) {
      this.adapters.add(adapter);
   }
}

分析DefaultAdvisorAdapterRegistry类的getInterceptors方法源码。可以发现在getInterceptors方法中主要做了以下几件事情。

  1. 实例化了一个MethodInterceptor类型拦截器链表interceptors。
  2. 获取入参Advisor通知器所对应的Advice通知。
  3. 判断这个Advice通知是不是MethodInterceptor类型的拦截器。如果是则加入到interceptors拦截器链表。
  4. 循环遍历已注册的适配器列表,判断这个Advice通知是不是适配器所支持的类型。如果是支持的话,则通过这个适配器把当前这个Advice通知装换成所对应的拦截器,然后放入到interceptors拦截器链表中。
  5. 如果经过以上4个步骤,interceptors拦截器链表还是为空,则抛出异常。
  6. 最后将得到的interceptors拦截器链表转换成拦截器数组返回。

通过AdvisorAdapterRegistry实例的getInterceptors方法,我们就可以获到取Advisor通知器所对应的拦截器数组。

3.2 MethodInvocation实例

通过3.1我们已经获取到了拦截器数组,那数组中的一个个拦截器又是在哪里进行调用的呢?其实Spring AOP是在MethodInvocation实例的proceed方法,执行了拦截器的invoke方法,对拦截器进行了回调。

MethodInvocation接口最终是继承了Joinpoint接口。所以说MethodInvocation实例其实是一个JoinPoint连接点,它是Spring AOP横切逻辑织入操作的执行点。

Spring AOP在JDK动态代理中,生成的MethodInvocation实例ReflectiveMethodInvocation对象。而通过CGLIB产生的MethodInvocation实例是CglibMethodInvocation对象。CglibMethodInvocation是ReflectiveMethodInvocation的子类。它们对拦截器链的调用都是在ReflectiveMethodInvocation中通过proceed方法来实现的。

下面我们来研究下ReflectiveMethodInvocation的proceed方法代码

java 复制代码
public Object proceed() throws Throwable {

   // We start with an index of -1 and increment early.
   //通过索引来判断拦截器列表中的拦截器是否已经全部调用完毕。
   // 如果调用完毕,则通过反射机制来调用目标对象的方法
   if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
      return invokeJoinpoint();
   }

   //通过索引递增,来获取列表中的拦截器。索引是从-1开始。
   Object interceptorOrInterceptionAdvice =
         this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
   //如果拦截器是否是InterceptorAndDynamicMethodMatcher类型的
   if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
      // Evaluate dynamic method matcher here: static part will already have
      // been evaluated and found to match.
      InterceptorAndDynamicMethodMatcher dm =
            (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
      Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
      //进行方法动态匹配的判断,若匹配,就执行advice通知的横切逻辑
      if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
         return dm.interceptor.invoke(this);
      }
      else {
         // Dynamic matching failed.
         // Skip this interceptor and invoke the next in the chain.
         //不匹配的话,递归调用proceed方法
         return proceed();
      }
   }
   else {
      // It's an interceptor, so we just invoke it: The pointcut will have
      // been evaluated statically before this object was constructed.
      //如果是一个MethodInterceptor类型的拦截器,则直接调用这个拦截器的invoke方法。
      return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
   }
}

ReflectiveMethodInvocation的proceed方法代码主要做了以下几件事情:

  1. 通过拦截器的索引来判断列表中的所有拦截器是否已经都已经调用完毕,如果调用完毕,则通过反射方式直接调用目标对象的方法返回。拦截器的索引是从-1开始的。
  2. 按照索引递增的顺序,来获取列表中的拦截器。
  3. 判断当前拦截器的类型,如果拦截器是InterceptorAndDynamicMethodMatcher类型。进行方法动态匹配的判断,若匹配,就执行advice通知的横切逻辑。不匹配的话,递归调用proceed方法。
  4. 如果是MethodInterceptor类型的拦截器,则直接调用这个拦截器的invoke方法。

总之,ReflectiveMethodInvocation对象的proceed方法,会先依次执行定义在代理对象上的拦截器。拦截器调用完成后,最后再调用代理对象的目标方法。这些拦截器定义了织入到代码中的横切逻辑,通过它们从而实现了AOP的功能。

4.AspectJ形式的Spring AOP

4.1 AspectJ的概述

AspectJ是一个面向切面的框架,目前是Java社区里最流行也是最完整的AOP框架。AspectJ定义了AOP语法,它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件。

AspectJ可以单独使用,也可以整合到其它框架中(比如Spring)。在Spring2.0版本发布之后,我们可以使用基于AspectJ注解或基于XML配置的AOP。本文着重介绍AspectJ注解@AspectJ。

@AspectJ注解是一种新的Spring AOP使用方式。我们定义一个@AspectJ 注解的POJO对象来代表一个AspectJ。在这个POJO对象中,我们可以定义一系列的方法,并在这些方法添加特定注解标注来代表一系列的Pointcut和Advice。

Spring AOP会根据@AspectJ注解来查找相关的Aspect定义,并将其声明的横切逻辑织入到当前系统中。其实就是把以前在xml中定义的通知和切点,现在放到了具有特定注解的POJO对象中,但是@AspectJ方式的最终的实现机制还是Spring AOP最初的架构,也就是使用代理模式处理横切逻辑的织入。

4.2 @AspectJ的使用

下面我们使用一个简单的例子,来看一下@AspectJ在Spring中具体是如何使用的。其实在Spring中要使用AspectJ的AOP功能,我们先定义一个有@Aspect 注解的POJO对象,在这个POJO对象中定义切点和通知。最后再开启AspectJ的AOP功能。

4.2.1定义POJO对象

示例中我们定义了一个AspectJDemo的POJO对象,该对象使用@Aspect进行注解。在这个对象中,我们可以定义Pointcut切点。并定义在这个切点上的前置,后置,环绕等横切逻辑。这些切点和横切逻辑其实都是加了特定注解的方法。

java 复制代码
@Aspect
@Component
public class AspectJDemo {
   //定义Pointcut切点,指定增强的横切逻辑在BeanDemo这个bean的所有方法中执行
   @Pointcut("execution(* com.xxx.aop.BeanDemo.*(..))")
   public void pointcutMothed() { 
      System.out.println("Pointcut");
   }

   //定义了前置的横切逻辑
    @Before("pointcutMothed()")
    public void before(){
       System.out.println("Before");
    }

    //定义了后置的横切逻辑
    @After("pointcutMothed()")
    public void after(){
        System.out.println("After");
    }

    @AfterReturning("pointcutMothed()")
    public void AfterReturning(){
       System.out.println("AfterReturning");
    }    

    //定义了环绕的横切逻辑
    @Around("pointcutMothed()")
    public Object around(ProceedingJoinPoint point){
       System.out.println("Around begin");
       Object o = null;
       try {
         o = point.proceed();
      } catch (Throwable e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
      }        
      System.out.println("Around end");     
      return o;
    }
}

4.2.2开启AspectJ功能

在Spring中要使用AspectJ的AOP功能的话,我们需要先对该功能进行开启。@EnableAspectJAutoProxy在这个注解就开启AOP功能的钥匙。

java 复制代码
@Configuration
@ComponentScan("com.xxx.aop")
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class BeanConfig {
}

4.2.3BeanDemo

BeanDemo这个对象是为演示本文中AOP的实例而定义的一个Bean。在AspectJDemo的POJO对象中我们定义了Pointcut切点。Pointcut注解的表达式是"execution(* com.xxx.aop.BeanDemo.*(..))",表示AOP的横切逻辑作用于BeanDemo这个对象的所有方法。

java 复制代码
@Component
public class BeanDemo {
   private String msg = "msg";

   public String getMsg() {
      return msg;
   }

   public void setMsg(String msg) {
      this.msg = msg;
   } 

   public void showMsg() {
      System.out.println(this.msg);
   }
}

4.2.4AopDemo示例

java 复制代码
public class AopDemo {

   public static void main(String[] args) {
      AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class);
      BeanDemo beanDemo = context.getBean("beanDemo",BeanDemo.class);
      beanDemo.showMsg();
   }
}

执行结果:

4.3AnnotationAwareAspectJAutoProxyCreator类

4.3.1注册AnnotationAwareAspectJAutoProxyCreator

Spring中要使用AspectJ的AOP,我们需要使用@EnableAspectJAutoProxy注解来开启这个功能。@EnableAspectJAutoProxy注解开启的实质是注册了AnnotationAwareAspectJAuto-

ProxyCreator类。

java 复制代码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
   boolean proxyTargetClass() default false;
   boolean exposeProxy() default false;
}

通过以上源码可以发现@EnableAspectJAutoProxy注解,通过@Import这个元注解导入AspectJAutoProxyRegistrar类。AspectJAutoProxyRegistrar类实现了ImportBeanDefinitionRegistrar接口,它覆写了接口的重要方法registerBeanDefinitions。

java 复制代码
@Override
public void registerBeanDefinitions(

   AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
   AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
   AnnotationAttributes enableAspectJAutoProxy =
         AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);

   if (enableAspectJAutoProxy != null) {
      if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
         AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
      }

      if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
         AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
      }
   }
}

从源码中,我们可以发现registerBeanDefinitions方法主要做了三件事情。

1.调用AopConfigUtils工具类的registerAspectJAnnotationAutoProxyCreatorIfNecessary方法,把AnnotationAwareAspectJAutoProxyCreator类的BeanDefinition信息注册到Sring容器。

java 复制代码
@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
      BeanDefinitionRegistry registry, @Nullable Object source) {
   return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}

通过上述方法,Sring就完成了AnnotationAwareAspectJAutoProxyCreator的注册。

2.给AnnotationAwareAspectJAutoProxyCreator类的BeanDefinition设置proxy-target-class属性。proxy-target-class属性主要用于Spring为目标对象创建代理对象时,是使用JDK动态代理,还是CGLIB动态字节码技术。

3.给AnnotationAwareAspectJAutoProxyCreator类的BeanDefinition设置expose-proxy属性。

expose-proxy属性用于是否将代理对象暴露给用户,如果暴露的话,则可以通过Spring AopContext类获得。

4.3.2 AnnotationAwareAspectJAutoProxyCreator类图

4.3.3 AnnotationAwareAspectJAutoProxyCreator工作原理

当Spring把AnnotationAwareAspectJAutoProxyCreator注册到容器之后,Annotation-

AwareAspectJAutoProxyCreator就做如下几个工作。

  • 获取定义的通知器。
  • 将通知器与定义的切点进行配置,过滤出匹配的通知器。
  • 使用匹配的通知器创建代理对象。

从以上类图中,我们可以发现AnnotationAwareAspectJAutoProxyCreator实现了BeanPostProcessor和InstantiationAwareBeanPostProcessor接口。所以当Spring加载AnnotationAwareAspectJAutoProxyCreator这个Bean的时候,会调用Bean实例化的前置方法和初始化后置方法。它们分别是InstantiationAwareBeanPostProcessor接口实例的postProcessBeforeInstantiation和BeanPostProcessor接口实例的postProcessAfter-

Initialization方法。这两方法是AnnotationAwareAspectJAutoProxyCreator工作的入口函数。

4.3.3.1 获取通知器

在@Aspect注解定义的AOP中,Spring是如何来获取通知器的呢?其实Spring主要是通过以下的几个步骤来获取通知器的。

  • 在Spring容器中,首先获取所有bean名称beanName。遍历所有的beanName,再通过beanName来获取相应的bean。
  • 对bean进行过滤的操作,判断bean是否带有Aspect的注解。如果是的话,这个bean就会被提取出来。其实这个bean就是上述AOP示例中定义的pojo对象。
  • 最后再从提取的bean中,把bean中定义的一系列的通知器给提取出来,并把这些提取的通知器给缓存起来。

下面我再从代码的角度,来看看Spring是如何上述的步骤来获取通知器的。AnnotationAwareAspectJAutoProxyCreator的父类AbstractAutoProxyCreator实现了BeanPostProcessor接口的postProcessAfterInitialization方法。

java 复制代码
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {

   if (bean != null) {
      Object cacheKey = getCacheKey(bean.getClass(), beanName);
      if (this.earlyProxyReferences.remove(cacheKey) != bean) {
         return wrapIfNecessary(bean, beanName, cacheKey);
      }
   }
   return bean;
}

在postProcessAfterInitialization方法中,会调用wrapIfNecessary方法。

java 复制代码
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {

   //如果已经为这个bean创建了代理对象,则直接返回。
   if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
      return bean;
   }

   //如果这个bean不需要被代理增强,则直接返回。
   if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
      return bean;
   }

   //1.如果这个bean的类型是基础设施类型的(Advice,Pointcut,Advisor这些类型),则直接返回。
   //2.通过shouldSkip这个函数来判断是否直接跳过,主要是判断bean是不是AspectJPointcutAdvisor类型的切点。
   if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
      this.advisedBeans.put(cacheKey, Boolean.FALSE);
      return bean;
   }

   // Create proxy if we have advice.
   //从bean中获取到已经匹配的通知器。
   Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
   //获取匹配的通知器不为空的情况
   if (specificInterceptors != DO_NOT_PROXY) {
      this.advisedBeans.put(cacheKey, Boolean.TRUE);
      //使用匹配的通知器来创建代理对象
      Object proxy = createProxy(
      bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
      this.proxyTypes.put(cacheKey, proxy.getClass());
      return proxy;
   }

   this.advisedBeans.put(cacheKey, Boolean.FALSE);
   return bean;
}

AbstractAutoProxyCreator还实现了InstantiationAwareBeanPostProcessor接口的postProcessBeforeInstantiation方法。

java 复制代码
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {

   //通过bean的Class和名称,来获取key。
   Object cacheKey = getCacheKey(beanClass, beanName);

   //beanName不为空,targetSourcedBeans不包含这个beanName
   if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
      //如果这个bean已经被代理增强,则直接返回。
      if (this.advisedBeans.containsKey(cacheKey)) {
         return null;
      }
      //1.如果这个bean的类型是基础设施类型的(Advice,Pointcut,Advisor这些类型),则直接返回。
      //2.通过shouldSkip这个函数来判断是否直接跳过,主要是判断bean是不AspectJPointcutAdvisor类型的切点。
      if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
         this.advisedBeans.put(cacheKey, Boolean.FALSE);
         return null;
      }
   }

   // Create proxy here if we have a custom TargetSource.
   // Suppresses unnecessary default instantiation of the target bean:
   // The TargetSource will handle target instances in a custom fashion.
   TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
   if (targetSource != null) {
      if (StringUtils.hasLength(beanName)) {
         this.targetSourcedBeans.add(beanName);
      }

      //从bean中获取到已经匹配的通知器。
      Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
      //使用匹配的通知器来创建代理对象
      Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
      this.proxyTypes.put(cacheKey, proxy.getClass());
      return proxy;
   }
   return null;
}

从代码中可以发现postProcessBeforeInstantiation方法和wrapIfNecessary方法的逻辑大体上是相似的。他们都调用了两个很重要的方法shouldSkip和getAdvicesAndAdvisorsForBean方法。而这两个方法又都调用了findCandidateAdvisors方法。而findCandidateAdvisors方法则是Sring中获取通知器的地方。

java 复制代码
public List<Advisor> buildAspectJAdvisors() {

   List<String> aspectNames = this.aspectBeanNames;
   //判断@Aspect注解缓存的beanName列表是否为空。
   //容器中@Aspect注解的beanName和这个Bean中定义的通知器,只会第一次获取后放入缓存,以后只从缓存中取。
   if (aspectNames == null) {
      synchronized (this) {
         aspectNames = this.aspectBeanNames;
         if (aspectNames == null) {
            List<Advisor> advisors = new ArrayList<>();
            aspectNames = new ArrayList<>();
            //获取Spring容器中所有Bean的名称
            String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                  this.beanFactory, Object.class, true, false);
            //循环遍历所有的Bean名称
            for (String beanName : beanNames) {
               //判断是否是合法的Bean,不合法则跳过循环。默认返回true,由其子类定义规则。
               if (!isEligibleBean(beanName)) {
                  continue;
               }

               // We must be careful not to instantiate beans eagerly as in this case they
               // would be cached by the Spring container but would not have been weaved.
               //通过Bean名称来获取到相应的Bean的类型
               Class<?> beanType = this.beanFactory.getType(beanName, false);
               if (beanType == null) {
                  continue;
               }

               //通过Bean的类型来判断这个Bean是否有@Aspect的注解
               if (this.advisorFactory.isAspect(beanType)) {
                  //如果是@Aspect注解的Bean,则把Bean的名称假如缓存
                  aspectNames.add(beanName);
                  AspectMetadata amd = new AspectMetadata(beanType, beanName);
                  if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
                     MetadataAwareAspectInstanceFactory factory =
                           new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);

                     //获取到@Aspect注解的Bean定义的通知器
                     List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
                     //将获取到的通知器加入到缓存中
                     if (this.beanFactory.isSingleton(beanName)) {
                        this.advisorsCache.put(beanName, classAdvisors);
                     }
                     else {
                        this.aspectFactoryCache.put(beanName, factory);
                     }
                     advisors.addAll(classAdvisors);
                  }
                  else {
                     // Per target or per this.
                     if (this.beanFactory.isSingleton(beanName)) {
                        throw new IllegalArgumentException("Bean with name '" + beanName +
                              "' is a singleton, but aspect instantiation model is not singleton");
                     }
                     MetadataAwareAspectInstanceFactory factory =
                           new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
                     this.aspectFactoryCache.put(beanName, factory);
                     //获取到@Aspect注解的Bean定义的通知器,并加入到通知器的列表
                     advisors.addAll(this.advisorFactory.getAdvisors(factory));
                  }
               }
            }
            this.aspectBeanNames = aspectNames;
            return advisors;
         }
      }
   }
   //Spring容器中,没有@Aspect的注解Bean的情况
   if (aspectNames.isEmpty()) {
      return Collections.emptyList();
   }
   //如果通知器已经缓存,则按BeanName从缓存中获取通知器
   List<Advisor> advisors = new ArrayList<>();
   for (String aspectName : aspectNames) {
      List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
      if (cachedAdvisors != null) {
         advisors.addAll(cachedAdvisors);
      }
      else {
         MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
         advisors.addAll(this.advisorFactory.getAdvisors(factory));
      }
   }
   return advisors;
}

通过分析findCandidateAdvisors方法的源码,发现它主要是在第一次的时候,在Spring容器中获取@Aspect注解的bean,并从这些bean中获取到定好的通知器。再把这些@Aspect注解的beanName和通知器缓存起来。以后就从缓存中来获取通知器。真正获取通知器的地方,其实是委托给了ReflectiveAspectJAdvisorFactory类的getAdvisors方法。

java 复制代码
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {

   //获取到@Aspect注解Bean的class
   Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
   //获取到@Aspect注解Bean的名称
   String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
   //对@Aspect注解Bean进行检验
   validate(aspectClass);

   MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
         new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);

   List<Advisor> advisors = new ArrayList<>();
   //一般通知器的获取
   //遍历@Aspect注解的Bean中,定义的所有的方法(排除掉@Pointcut注解的方法),这些方法定义了AOP的横切逻辑。
   for (Method method : getAdvisorMethods(aspectClass)) {
      //通过@Aspect注解的Bean中定义的方法,来获取通知器
      Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);
      if (advisor != null) {
         advisors.add(advisor);
      }
   }
   //如果通知器列表不为空而且又配置了增强延迟初始化
   if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
      //那么就需要在通知器列表的首位,加入同步实例化通知器SyntheticInstantiationAdvisor。
      Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
      advisors.add(0, instantiationAdvisor);
   }

   //获取@DeclareParents注解定义的字段
   for (Field field : aspectClass.getDeclaredFields()) {
      //通过字段构造DeclareParentsAdvisor类型的通知器
      Advisor advisor = getDeclareParentsAdvisor(field);
      if (advisor != null) {
         advisors.add(advisor);
      }
   }
   return advisors;
}

以上getAdvisors方法中,主要是三类通知器的获取:

  • 一般通知器的获取,通过@Aspect注解Bean中定义的方法来获取通知器。这些方法排除掉了@Pointcut注解的方法。
  • SyntheticInstantiationAdvisor通知器的获取,在通知器列表不为空而且又配置了增强延迟初始化的情况下,通知器列表的首位加入实例化的SyntheticInstantiationAdvisor通知器。
  • DeclareParentsAdvisor通知器的获取,如果@Aspect注解Bean中,定义了@DeclareParents注解定义的字段。则返回DeclareParentsAdvisor类型的通知器。

下面我们着重分析一下一般通知器的获取,一般通知器是通过getAdvisor方法来获取。getAdvisor方法如下:

java 复制代码
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,int declarationOrderInAspect, String aspectName) {

   validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
   //构造Pointcut切点
   AspectJExpressionPointcut expressionPointcut = getPointcut(
         candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
   if (expressionPointcut == null) {
      return null;
   }

   //通过Pointcut切点和Method来构建通知器
   return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}

一般通知器是由Pointcut切点和Advice通知构成。上述代码通过getPointcut方法返回了AspectJExpressionPointcut类型的切点。然后通过这个切点和Method来构建InstantiationModelAwarePointcutAdvisorImpl类型的通知器。那么具体Advice通知构建是在InstantiationModelAwarePointcutAdvisorImpl类的instantiateAdvice方法的完成的。

java 复制代码
private Advice instantiateAdvice(AspectJExpressionPointcut pointcut) {
   Advice advice = this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pointcut,
         this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
   return (advice != null ? advice : EMPTY_ADVICE);
}

我们分析instantiateAdvice方法的源码可以看出,该方法获取通知其实是委托给了ReflectiveAspectJAdvisorFactory类的getAdvice方法。

java 复制代码
public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {

   //获取到@Aspect注解的Bean的class
   Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();

   //对@Aspect注解的Bean进行检验
   validate(candidateAspectClass);

   //获取到@Aspect注解的Bean中方法上,定义的注解。
   AspectJAnnotation<?> aspectJAnnotation =
       AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
   if (aspectJAnnotation == null) {
      return null;
   }

   // If we get here, we know we have an AspectJ method.
   // Check that it's an AspectJ-annotated class
   //检查这个class是否有@Aspect注解,如果没有则抛出异常。
   if (!isAspect(candidateAspectClass)) {
      throw new AopConfigException("Advice must be declared inside an aspect type: " +
            "Offending method '" + candidateAdviceMethod + "' in class [" +
            candidateAspectClass.getName() + "]");
   }

   if (logger.isDebugEnabled()) {
      logger.debug("Found AspectJ method: " + candidateAdviceMethod);
   }

   AbstractAspectJAdvice springAdvice;
   //更加注解的类型,来实例化不同类型的通知并返回。
   //例如:注解的类型是Before,则构建AspectJMethodBeforeAdvice类型的通知
   switch (aspectJAnnotation.getAnnotationType()) {
      case AtPointcut:
         if (logger.isDebugEnabled()) {
            logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
         }
         return null;
      case AtAround:
         springAdvice = new AspectJAroundAdvice(
               candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
         break;
      case AtBefore:
         springAdvice = new AspectJMethodBeforeAdvice(
               candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
        break;
      case AtAfter:
         springAdvice = new AspectJAfterAdvice(
               candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
         break;
      case AtAfterReturning:
         springAdvice = new AspectJAfterReturningAdvice(
               candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
         AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
         if (StringUtils.hasText(afterReturningAnnotation.returning())) {
            springAdvice.setReturningName(afterReturningAnnotation.returning());
         }
         break;
      case AtAfterThrowing:
         springAdvice = new AspectJAfterThrowingAdvice(
               candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
         AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
         if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
            springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
         }
         break;
      default:
         throw new UnsupportedOperationException(
               "Unsupported advice type on method: " + candidateAdviceMethod);
   }

   // Now to configure the advice...
   springAdvice.setAspectName(aspectName);
   springAdvice.setDeclarationOrder(declarationOrder);
   String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
   if (argNames != null) {
      springAdvice.setArgumentNamesFromStringArray(argNames);
   }
   springAdvice.calculateArgumentBindings();
   return springAdvice;
}

在以上getAdvice方法的代码,我们看到了真正构建通知的方法。getAdvice方法中,首先会获取到通过@Aspect注解Bean中方法上的注解。再根据注解类型的不同,来构建不同类型的AspectJ的通知,并返回。

例如:如果获取的注解是Before类型,代码中就会构建AspectJMethodBeforeAdvice类型的通知,并返回。

4.3.3.2 通知器的匹配

通过上面的步骤,Spring中已经获取到了容器中定义的所有通知器。但是对于当前的Bean, Spring还需要通过切点过滤出与当前的Bean匹配的通知器。

对通知器的匹配,Spring是在findAdvisorsThatCanApply方法实现的。

java 复制代码
protected List<Advisor> findAdvisorsThatCanApply(
      List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {

   ProxyCreationContext.setCurrentProxiedBeanName(beanName);
   try {
      return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
   }
   finally {
      ProxyCreationContext.setCurrentProxiedBeanName(null);
   }
}

通过findAdvisorsThatCanApply的代码发现,通知器的匹配其实是委托给了AopUtils工具类的findAdvisorsThatCanApply方法。

java 复制代码
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
   //如果通知器列表为空,则直接返回
   if (candidateAdvisors.isEmpty()) {
      return candidateAdvisors;
   }

   List<Advisor> eligibleAdvisors = new ArrayList<>();
   for (Advisor candidate : candidateAdvisors) {
      //对IntroductionAdvisor类型的通知器进行匹配处理
      if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
         eligibleAdvisors.add(candidate);
      }
   }

   boolean hasIntroductions = !eligibleAdvisors.isEmpty();
   for (Advisor candidate : candidateAdvisors) {
      //如果是IntroductionAdvisor类型的通知器,则直接跳过
      if (candidate instanceof IntroductionAdvisor) {
         // already processed
         continue;
      }
      //对一般的通知器进行匹配处理
      if (canApply(candidate, clazz, hasIntroductions)) {
         eligibleAdvisors.add(candidate);
      }
   }
   return eligibleAdvisors;
}

findAdvisorsThatCanApply方法主要是把IntroductionAdvisor类型的通知器和一般的通知器区分开来,分别进行处理。真正进行匹配的方法是canApply方法。

java 复制代码
public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
   //对IntroductionAdvisor类型的通知器,直接进行类型级别匹配
   if (advisor instanceof IntroductionAdvisor) {
      return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
   }
   //对PointcutAdvisor类型的通知器,再次调用canApply重载方法进行匹配
   else if (advisor instanceof PointcutAdvisor) {
      PointcutAdvisor pca = (PointcutAdvisor) advisor;
      return canApply(pca.getPointcut(), targetClass, hasIntroductions);
   }
   else {
      // It doesn't have a pointcut so we assume it applies.
      return true;
   }
}

在canApply方法中,对IntroductionAdvisor类型的通知器,直接进行类型级别匹配。如果是PointcutAdvisor类型的通知器,再次调用canApply重载方法进行匹配。其它的类型则都作为已匹配。

canApply重载方法代码如下:

java 复制代码
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {

   Assert.notNull(pc, "Pointcut must not be null");
   //首先对切点进行类型级别的匹配
   if (!pc.getClassFilter().matches(targetClass)) {
      return false;
   }

   //获取切点上的方法匹配器
   MethodMatcher methodMatcher = pc.getMethodMatcher();
   //方法匹配器是TrueMethodMatcher类型的实例,则直接匹配通过
   if (methodMatcher == MethodMatcher.TRUE) {
      // No need to iterate the methods if we're matching any method anyway...
      return true;
   }

   //判断是否是IntroductionAwareMethodMatcher类型的方法匹配器
   IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
   if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
      introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
   }

   Set<Class<?>> classes = new LinkedHashSet<>();
   if (!Proxy.isProxyClass(targetClass)) {
      classes.add(ClassUtils.getUserClass(targetClass));
   }

   classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
   for (Class<?> clazz : classes) {
      Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
      for (Method method : methods) {
         //进行方法级别匹配操作
         //区分出IntroductionAwareMethodMatcher类型方法匹配器,进行特殊处理
         if (introductionAwareMethodMatcher != null ?
               introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
               methodMatcher.matches(method, targetClass)) {
               return true;
         }
      }
   }
   return false;
}
4.3.33 创建代理对象

在获取到bean对应的通知器后,下一步就是创建代理对象了。对于代理类的创建及处理,Spring是委托给了ProxyFactory类来处理的。在AbstractAutoProxyCreator类的postProcessBeforeInstantiation方法和postProcessAfterInitialization方法都会直接或者间接的调用createProxy方法来创建代理对象。

java 复制代码
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
      @Nullable Object[] specificInterceptors, TargetSource targetSource) {

   if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
      AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
   }

   //创建ProxyFactory对象
   ProxyFactory proxyFactory = new ProxyFactory();
   //复制当前类中相关的属性
   proxyFactory.copyFrom(this);
   //如果ProxyTargetClass标识设置为true的情况
   if (proxyFactory.isProxyTargetClass()) {
      // Explicit handling of JDK proxy targets (for introduction advice scenarios)
      //判断bean是否是Proxy类型
      if (Proxy.isProxyClass(beanClass)) {
         // Must allow for introductions; can't just set interfaces to the proxy's interfaces only.
         for (Class<?> ifc : beanClass.getInterfaces()) {
            //添加代理接口
            proxyFactory.addInterface(ifc);
         }
      }
   }
   else {
      // No proxyTargetClass flag enforced, let's apply our default checks...
      //决定给定的bean是否应该使用其目标类而不是其接口来进行代理
      if (shouldProxyTargetClass(beanClass, beanName)) {
         //如果是要对目标类进行代理,则把ProxyTargetClass标志设置为true
         proxyFactory.setProxyTargetClass(true);
      }
      else {
         //如果是要对目标类的接口进行代理的话
         //如果目标类中存在合理的代理接口,则添加代理接口
         evaluateProxyInterfaces(beanClass, proxyFactory);
      }
   }

   //调用DefaultAdvisorAdapterRegistry类的wrap方法对拦截器进行包装处理成通知器
   Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
   //添加通知器
   proxyFactory.addAdvisors(advisors);
   //设置需要代理的类
   proxyFactory.setTargetSource(targetSource);
   //留给子类来自定义设置proxyFactory对象
   customizeProxyFactory(proxyFactory);
   //用来控制代理工厂被配置之后,是否还允许修改通知
   proxyFactory.setFrozen(this.freezeProxy);
   if (advisorsPreFiltered()) {
      proxyFactory.setPreFiltered(true);
   }

   //返回创建的代理对象
   return proxyFactory.getProxy(getProxyClassLoader());
}

通过分析createProxy方法的源码,发现createProxy方法主要是做了以下几件事情:

  • 创建ProxyFactory对象。
  • ProxyFactory对象复制当前类中相关的属性。
  • 判断ProxyTargetClass标识,来添加代理接口。判断bean是否应该使用其目标类而不是其接口来进行代理,来设置ProxyTargetClass标识。
  • 调用DefaultAdvisorAdapterRegistry类的wrap方法把拦截器包装成通知器。
  • ProxyFactory对象添加包装好的通知器。
  • ProxyFactory对象设置要代理的目标类。
  • 调用customizeProxyFactory(proxyFactory)方法,该方法是留给来子类设置proxyFactory对象。
  • 调用ProxyFactory对象getProxy方法来返回代理对象。
相关推荐
JAVA学习通1 小时前
【JavaEE进阶】图书管理系统(未完待续)
java·spring·java-ee
人生偌只如初见1 小时前
SpringAI学习笔记-MCP客户端简单示例
java·spring·ai·client·mcp
Super Rookie3 小时前
Spring Cloud 企业项目技术选型
后端·spring·spring cloud
山海上的风12 小时前
Spring Batch终极指南:原理、实战与性能优化
spring·性能优化·batch·springbatch
找不到、了13 小时前
Spring的Bean原型模式下的使用
java·spring·原型模式
超级小忍14 小时前
Spring AI ETL Pipeline使用指南
人工智能·spring
Boilermaker199217 小时前
【Java EE】SpringIoC
前端·数据库·spring
写不出来就跑路17 小时前
Spring Security架构与实战全解析
java·spring·架构
sleepcattt18 小时前
Spring中Bean的实例化(xml)
xml·java·spring
小七mod18 小时前
【Spring】Java SPI机制及Spring Boot使用实例
java·spring boot·spring·spi·双亲委派