深入理解Spring(四)、AOP源码分析

>>强大,10k+点赞的 SpringBoot 后台管理系统竟然出了详细教程!

前言

深入理解Spring源码分为7小节,本小节为Spring源码第四小节,各个小节目录如下。

  1. 扫描过程
  2. bean创建过程
  3. 容器扩展

4.  AOP源码分析

  1. 事务源码分析
  2. Spring JDBC源码分析
  3. Spring常用设计模式

深入@Configuration代理

BeanDefinition接口实现了AttributeAccessor接口,这个接口用来设置或者获取一个属性,也就是说,每个Bean都可以有很多属性,而在配置类解析的时候,如果发现这个类有@Configuration注解,并且proxyBeanMethodss是true,那么会设置这个bean一个属性,这个属性叫org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass,值是full,而其他bean的这个属性值是lite。

这个属性用于表示未来会为这个类生成代理类并放入容器。

Spring内部会有一个BeanFactoryPostProcessor的实现类ConfigurationClassPostProcessor,当所有bean信息收集完成后,会被调用,他内部有个方法叫enhanceConfigurationClasses(),可以理解为增强配置类。

内部主要做的是筛选出属性名为org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass,并且值是full的bean,准备为他生产代理类,这就需要借助Enhancer,Enhancer会为原本的类生成一个代理类,并把新的类Class覆盖掉原本的。

比如原本类是com.xxx.TestConfig,经过这个阶段后,这个类变成了com.xxx.TestConfig$$EnhancerBySpringCGLIB$$

那么我们最关心的还是为这种类生成代理后要干嘛,比如调用一个方法时,Spring又做了哪些事?

所以我们要找到这个代理类的方法拦截里面的逻辑,需要对Enhancer有所了解,下面就是为类生成Enhancer的代码,主要关心setCallbackFilter、setCallbackTypes即可。

private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
   Enhancer enhancer = new Enhancer();
   enhancer.setSuperclass(configSuperClass);
   enhancer.setInterfaces(new Class<?>[]{EnhancedConfiguration.class});
   enhancer.setUseFactory(false);
   enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
   enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
   enhancer.setCallbackFilter(CALLBACK_FILTER);
   enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
   return enhancer;
}

Enhancer这里不说了,CALLBACKS这个数组保存的就是将来方法被调用时候的拦截器,但这三个拦截器不会应用于所有方法,需要挑选出来,通过setCallbackFilter完成,前两个拦截器都实现了ConditionalCallback接口,用来判断自己可不可以应用于这个方法。

private static final Callback[] CALLBACKS = new Callback[]{
      new BeanMethodInterceptor(),
      new BeanFactoryAwareMethodInterceptor(),
      NoOp.INSTANCE
};
private static final ConditionalCallbackFilter CALLBACK_FILTER = new ConditionalCallbackFilter(CALLBACKS);

我们从BeanMethodInterceptor开始看,他所拦截的方法要求所在的不是Object类并且不是BeanFactoryAware接口下的setBeanFactory方法并且有Bean注解,那么可能猜到,他对有@Bean注解的方法可能存在兴趣,要拦截。

@Override
public boolean isMatch(Method candidateMethod) {
   /**
    * 不是Object类 && 不是BeanFactoryAware接口下的setBeanFactory方法,&& 有Bean注解
    *
    */

   boolean b = candidateMethod.getDeclaringClass() != Object.class &&
         !BeanFactoryAwareMethodInterceptor.isSetBeanFactory(candidateMethod) &&
         BeanAnnotationHelper.isBeanAnnotated(candidateMethod)
;
   return b;
}

拦截后的逻辑主要在intercept()中,

这里大概说一下具体干了什么,由于@Configuration类下可能有@Bean方法,那么这个方法返回值最终也会被Spring收入到容器,全局唯一,如果我们把它当成普通方法来看,是不是每调用一次都会得到新的对象,那么在Spring管理范围内,不允许出现这种情况,那么这个拦截器主要的作用就是从容器中取出并返回,调用多次,实例还是一样的,当然还有其他情况,以后会细说。

在看BeanFactoryAwareMethodInterceptor,他拦截的是BeanFactoryAware接口下的setBeanFactory方法,这个我们可能还用不到,是Spring内部使用的,主要工作是对代理类中的$$beanFactory字段赋值,就不看了。

如果想查看CGLIB生成的类,可以通过下面语句,生成的class会保存到指定路径下。

System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/home/HouXinLin/test/cglib");

最后的NoOp.INSTANCE表示不拦截,直接调用父类。

深入@Aspect

在了解这个之前,我们先熟悉一下几个概念。

  1. 通知(Advice):

    A类下有个getUser()方法,在调用getUser()之前或者之后,想执行一些代码,那就需要通过@Before、@After这些注解定义方法,调用getUser()时就会执行到这里,这就是通知的意思,原方法调用时,通知到其他地方,Spring中抽象成Advice,具体实现有AspectJMethodBeforeAdvice、AspectJAfterAdvice等。后面所用到通知这一词,都是指这个意思。

  2. 连接点(Join Point)&切入点(Pointcut Point)

    这两个不太好理解,但我在stackoverflow找到了一个很好的例子, 当你去餐厅时,你会查看菜单,有很多你的候选项,你可以选择菜单上的任何一种或者多种,那么,连接点就是菜单上的候选项,切入点就是你选择的菜品。

拿实际举个例子。

class Employee{
    public String getName(){....}
    public void setName(String name){...}
}

这些方法都称为连接点,当我们只对getName()方法进行拦截时,那么这个方法可以叫做切入点,切入点可以通过正则表示,比如特定包中的所有方法、所有返回值是void的方法、所有set开头的方法。

  1. 通知器:

通知器不是AOP中的概念,是Spring中的特定术语,组合通知和切入点成为一个单元,并将其传递给ProxyFactory,他的接口是PointcutAdvisor,如下。

public interface PointcutAdvisor {

  Advice getAdvice();

  Pointcut getPointcut();
}

所有通知器都是PointcutAdvisor的实例。

下面做一个例子,这段代码也是Spring AOP的核心,他会拦截TestLog下的print方法,在方法之前做一些事,MethodBeforeAdvice相当于@Before,但是Spring在内部实例化的是AspectJMethodBeforeAdvice。

@Configuration
public class TestLog {
   public void print() {
   }
}
 MethodBeforeAdvice advice = new MethodBeforeAdvice() {
   @Override
   public void before(Method method, Object[] args, Object target) throws Throwable {
      System.out.println("before");
   }
};
final NameMatchMethodPointcutAdvisor nameMatchMethodPointcutAdvisor = new NameMatchMethodPointcutAdvisor();
nameMatchMethodPointcutAdvisor.setMappedName("print");
nameMatchMethodPointcutAdvisor.setAdvice(advice);
final ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.addAdvisor(nameMatchMethodPointcutAdvisor);
proxyFactory.setTarget(new TestLog());

TestLog proxy = (TestLog) proxyFactory.getProxy();
proxy.print();

在看下另一个关键代码,这个代码解决的问题是:这个切入点可不可以应用于某个方法上,这在后面是非常重要的。

boolean matches = nameMatchMethodPointcutAdvisor.getPointcut().getMethodMatcher().matches(log, TestLog.class);
System.out.println(matches);

下面我们正式开始Spring的AOP。

使用AOP,都会从这两个注解开始,@Aspect、@EnableAspectJAutoProxy,先说@EnableAspectJAutoProxy,他的定义如下。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)

看到@Import了吧,在前面说过,在Spring第二次收集bean的过程中会解析所有@Import注解,并从里面收集新的bean,所以在来看AspectJAutoProxyRegistrar,他实现了ImportBeanDefinitionRegistrar接口,意味着Spring会在某个时间点调用里面的registerBeanDefinitions()方法来进行收集。

而AspectJAutoProxyRegistrar类中把AnnotationAwareAspectJAutoProxyCreator封装为BeanDifinition添加到容器,这个类非常关键。

他实现了BeanPostProcessor接口,意味着可以在某个对象创建后对其进行更改。

他实现了BeanFactoryAware接口,意味着Spring会把BeanFactory告诉他,拿到BeanFactory的bean对容器的控制权很大。

他实现了InstantiationAwareBeanPostProcessor接口,意味着可以在Spring实例化某个对象之前,先手一步进行实例化,阻断后续对象创建过程。

我们只关注他对BeanPostProcessor的postProcessAfterInitialization()方法实现即可。

@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);//创建代理对象,如果这个bean有被代理的资格
      }
   }
   return bean;
}

wrapIfNecessary()方法用于如果这个对象需要被代理,那么就生成代理对象并返回。

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
 
   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;
   }
   return bean;
}

这里关键点是getAdvicesAndAdvisorsForBean()和createProxy(),getAdvicesAndAdvisorsForBean()会获取有关于指定bean的通知信息,如果结果不为null,则表示至少有一个通知可以应用到这个bean,那么就会通过createProxy()创建代理对象。

进入getAdvicesAndAdvisorsForBean()后没有具体逻辑,具体逻辑一路调用到下面这个方法,这个方法已经是经过删减的了,这样逻辑更清晰。

但是当调用顺序为我们所讲的这样的话,并不会进入if里面,这是由于先前已经有个地方调用过这里了,他调用时候aspectNames是null,然后才会进一步解析,解析后会缓存起来,后续调用的话直接从缓存获取,这个地方还是在InstantiationAwareBeanPostProcessor接口下,实现类还是上面说的AnnotationAwareAspectJAutoProxyCreator

public List<Advisor> buildAspectJAdvisors() {
   /**
    * 所有具有Aspect注解的bean
    */

   List<String> aspectNames = this.aspectBeanNames;
   if (aspectNames == null) {
      synchronized (this) {
         aspectNames = this.aspectBeanNames;
         if (aspectNames == null) {
            List<Advisor> advisors = new ArrayList<>();
            aspectNames = new ArrayList<>();
            /**
             * 所有bean名称
             */

            String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                  this.beanFactory, Object.classtruefalse);
            for (String beanName : beanNames) {
               if (!isEligibleBean(beanName)) {
                  continue;
               }
              
               /**
                * 如果这类类是一个aspect类
                */

               if (this.advisorFactory.isAspect(beanType)) {
                  aspectNames.add(beanName);
                  AspectMetadata amd = new AspectMetadata(beanType, beanName);
                  if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
                     MetadataAwareAspectInstanceFactory factory =
                           new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
                     /**
                      * 从这里获取所有增强
                      */

                     List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);

                     advisors.addAll(classAdvisors);
                  }
                 
               }
            }
            this.aspectBeanNames = aspectNames;
            return advisors;
         }
      }
   }
   if (aspectNames.isEmpty()) {
      return Collections.emptyList();
   }
   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;
}

我们继续看他具体的逻辑,下面这句是关键,这个方法用来获取这个bean中所有切入点和通知组合的Advisor信息,一个类中可以定义多个通知,就是可能有多个@Before或者@After。

List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);

对于bean中的方法,通过下面这个方法提取信息,getPointcut()用来在方法上提取 @Pointcut, @Around, @Before, @After, @AfterReturning, @AfterThrowing注解,如果有的话。

public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
      int declarationOrderInAspect, String aspectName)
 
{

   /**
    * 从这里在方法试图提取切入点信息
    */

   AspectJExpressionPointcut expressionPointcut = getPointcut(
         candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
   if (expressionPointcut == null) {
      return null;
   }
   /**
    * 找到的话封装为InstantiationModelAwarePointcutAdvisorImpl
    *
    * InstantiationModelAwarePointcutAdvisorImpl的构造方法中会构建出通知
    */

   return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
         this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}

提取后返回InstantiationModelAwarePointcutAdvisorImpl,但这个对象的构造方法中还做了一件事。

/**
 * 更具注解生产不同的Advice
 */

this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);

instantiateAdvice会调用到这里。


@Override
@Nullable
public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
      MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName)
 
{
   /**
    * 这里只有一处调用,在InstantiationModelAwarePointcutAdvisorImpl中
    */

   Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
   //获取方法上注解的类型
   AspectJAnnotation<?> aspectJAnnotation =
         AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
   /**
    * 根据不同的注解生产不同的Advice
    */

   AbstractAspectJAdvice springAdvice;
   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;
}

到这里接结束了,现在的结果是所有标有@Aspect类下的通知器信息,通知器都实现了Advisor接口,这里都是InstantiationModelAwarePointcutAdvisorImpl的实现类。

InstantiationModelAwarePointcutAdvisorImpl中包含着切入点、通知等信息,但这还不能直接返回,因为这些信息可能对当前类没用,不需要代理,所以还少一步是从这些信息中提取能在本类用上的,这部分以后说。

现在到了为对象创建代理类的时候了,假设已经了解了Enhancer,在通过getProxy()获取对象时,最重要的还是给他设置回调,默认有7个Callback,这7个不能每个都作用于方法上,所以要通过ProxyCallbackFilter过滤,如果调用的是我们自定义的方法,那么最终都会进入DynamicAdvisedInterceptor,还有剩余的6个Callback暂时未看具体是什么。

//经过删减
public Object getProxy(@Nullable ClassLoader classLoader) {

   try {
       //创建Enhancer
      Enhancer enhancer = createEnhancer();
       //生成的代理类集成这个类
      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();
      }
      //设置Callback过滤器
      enhancer.setCallbackFilter(new ProxyCallbackFilter(
            this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
      enhancer.setCallbackTypes(types);
      return createProxyClassAndInstance(enhancer, callbacks);
   }
   catch (CodeGenerationException | IllegalArgumentException ex) {
   }
   catch (Throwable ex) {
   }
}

到了最后一点了,Spring AOP的执行过程是个链式的,所以,会有一个方法来获取所有链,先看DynamicAdvisedInterceptor#intercept方法。

List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

Object retVal;
if (chain.isEmpty() && CglibMethodInvocation.isMethodProxyCompatible(method)) {
   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 {
   retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}

第一句就是获取所有能作用于这个方法的拦截链,如果调用this.advised.getAdvisors()那么返回的都是能作用于当前方法上的所有通知,getInterceptorsAndDynamicInterceptionAdvice()用于把所有通知转换成Interceptor,然后按顺序依次调用,比如@Before会被转换成MethodBeforeAdviceInterceptor,说是转换,其实内部是提取Advisor中的信息做包装。

要注意的是,所有链中Spring都会在第一个位置添加ExposeInvocationInterceptor链,所以进入的第一个链都是他。

控制链的走向在ReflectiveMethodInvocation的proceed方法中,比较简单,这里就不说了。

- END -


原文始发于微信公众号(十四个字节):深入理解Spring(四)、AOP源码分析