Spring AOP中是如何注册Advisor的?

简介: Spring AOP中是如何注册Advisor的?

我们看到创建代理前首先要获取到Advisor设置给ProxyFactory,之后才可进行代理的创建。那么容器中的Advisor是如何实例化并注册的?


这个最开始的入口是在AbstractAutoProxyCreator的postProcessBeforeInstantiation方法中。


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


【1】前置流程

如下图所示,在AbstractApplicationContext的refresh方法中会触发registerBeanPostProcessors(beanFactory);进行Bean后置处理器的注册。


而在AbstractAutowireCapableBeanFactory的createBean过程中,会触发resolveBeforeInstantiation(beanName, mbdToUse);,那么这里就会触发applyBeanPostProcessorsBeforeInstantiation。


AbstractAutowireCapableBeanFactory的applyBeanPostProcessorsBeforeInstantiation方法如下所示。

@Nullable
protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
  for (BeanPostProcessor bp : getBeanPostProcessors()) {
    if (bp instanceof InstantiationAwareBeanPostProcessor) {
      InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
      Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
      if (result != null) {
        return result;
      }
    }
  }
  return null;
}

这里会判断BeanPostProcessor 是否为InstantiationAwareBeanPostProcessor,如果是,则调用其postProcessBeforeInstantiation方法。


InstantiationAwareBeanPostProcessor是BeanPostProcessor的一个子接口,在bean的属性设置或autowire触发前增加before-instantiation和after-instantiation回调。通常用于抑制特定目标bean的默认实例化,例如创建具有特殊TargetSources的代理(池目标、延迟初始化目标等),或实现其他注入策略,如字段注入。


如下所示是其子类实现,这里我们主要分析AbstractAutoProxyCreator。

如下图所示,AbstractAutoProxyCreator的postProcessBeforeInstantiation会首先对bean进行判断。isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName),前者用来判断是否为基础类,后置用来判断是否需要直接跳过。


当前这只是语义含义,实际上isInfrastructureClass用来判断当前bean是否为Advice、Pointcut、Advisor、AopInfrastructureBean或者(标注了Aspect注解但是bean的field不是以ajc$开头)。


我们看下shouldSkip方法。这里会调用AspectJAwareAdvisorAutoProxyCreator的shouldSkip方法,如下所示。

@Override
protected boolean shouldSkip(Class<?> beanClass, String beanName) {
  // TODO: Consider optimization by caching the list of the aspect names
  List<Advisor> candidateAdvisors = findCandidateAdvisors();
  //如果advisor是AspectJPointcutAdvisor 且切面名称是beanName则跳过
  for (Advisor advisor : candidateAdvisors) {
    if (advisor instanceof AspectJPointcutAdvisor &&
        ((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {
      return true;
    }
  }
  return super.shouldSkip(beanClass, beanName);
}
//super.shouldSkip(beanClass, beanName); AbstractAutoProxyCreator#shouldSkip
protected boolean shouldSkip(Class<?> beanClass, String beanName) {
  //是否为 original instance
  return AutoProxyUtils.isOriginalInstance(beanName, beanClass);
}


很显然,advisor就是从findCandidateAdvisors方法得到的。

【2】得到Advisor


结合前面流程,这里会走到AnnotationAwareAspectJAutoProxyCreator的findCandidateAdvisors方法。也就说这个方法才是真正获取(实例化)Advisor的方法。


同样,在Spring AOP中如何为Bean创建代理?一文中为Bean创建代理获取Advisor时也会触发该方法。

@Override
protected List<Advisor> findCandidateAdvisors() {
  // Add all the Spring advisors found according to superclass rules.
  List<Advisor> advisors = super.findCandidateAdvisors();
  // Build Advisors for all AspectJ aspects in the bean factory.
  if (this.aspectJAdvisorsBuilder != null) {
    advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
  }
  return advisors;
}
this.aspectJAdvisorsBuilder =
new BeanFactoryAspectJAdvisorsBuilderAdapter(beanFactory, this.aspectJAdvisorFactory);


首先调用父类的findCandidateAdvisors方法,然后使用BeanFactoryAspectJAdvisorsBuilderAdapter创建AspectJAdvisor。

① super.findCandidateAdvisors();

这里是AbstractAdvisorAutoProxyCreator的findCandidateAdvisors方法。

//AbstractAdvisorAutoProxyCreator#findCandidateAdvisors
protected List<Advisor> findCandidateAdvisors() {
  Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available");
  return this.advisorRetrievalHelper.findAdvisorBeans();
}
// advisorRetrievalHelper是BeanFactoryAdvisorRetrievalHelperAdapter;

我们跟踪看一下advisorRetrievalHelper是如何找到Advisor的。

public List<Advisor> findAdvisorBeans() {
  // Determine list of advisor bean names, if not cached already.
  // 从缓存里面获取
  String[] advisorNames = this.cachedAdvisorBeanNames;
  if (advisorNames == null) {
    // Do not initialize FactoryBeans here: We need to leave all regular beans
    // uninitialized to let the auto-proxy creator apply to them!
    // 尝试获取Advisor 如internalTransactionAdvisor
    advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
        this.beanFactory, Advisor.class, true, false);
    this.cachedAdvisorBeanNames = advisorNames;
  }
  if (advisorNames.length == 0) {
    return new ArrayList<>();
  }
  List<Advisor> advisors = new ArrayList<>();
  for (String name : advisorNames) {
  // 这里默认返回true
    if (isEligibleBean(name)) {
      // 如果是正在创建的,则跳过
      if (this.beanFactory.isCurrentlyInCreation(name)) {
        if (logger.isTraceEnabled()) {
          logger.trace("Skipping currently created advisor '" + name + "'");
        }
      }
      else {
        try {
        // 从容器中获取Bean
          advisors.add(this.beanFactory.getBean(name, Advisor.class));
        }
        catch (BeanCreationException ex) {
          Throwable rootCause = ex.getMostSpecificCause();
          // ....
          throw ex;
        }
      }
    }
  }
  return advisors;
}

总结来讲该方法就是从容器中找Advisor,如果正在创建则跳过否则就获取到然后返回。

② aspectJAdvisorsBuilder.buildAspectJAdvisors


BeanFactoryAspectJAdvisorsBuilder的buildAspectJAdvisors方法如下所示。我们的切片被包装成Advisor就是在这里实现的。

public List<Advisor> buildAspectJAdvisors() {
  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,本文这里有460个
        String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
            this.beanFactory, Object.class, true, false);
        for (String beanName : beanNames) {
 //AnnotationAwareAspectJAutoProxyCreator.isEligibleAspectBean进行判断,默认返回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.
          // 获取beanTYPE
          Class<?> beanType = this.beanFactory.getType(beanName);
          if (beanType == null) {
            continue;
          }
          // 如果当前Bean标注了Aspect注解 且未被ajc编译
          if (this.advisorFactory.isAspect(beanType)) {
            // 放到aspectNames中-包含单例和非单例
            aspectNames.add(beanName);
            AspectMetadata amd = new AspectMetadata(beanType, beanName);
            // 默认就是SINGLETON
            if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
              MetadataAwareAspectInstanceFactory factory =
                  new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
              List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
              if (this.beanFactory.isSingleton(beanName)) {
              // 如果是单例则放入缓存
                this.advisorsCache.put(beanName, classAdvisors);
              }
              else {
              // 否则放入aspectFactoryCache
                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);
              advisors.addAll(this.advisorFactory.getAdvisors(factory));
            }
          }
        }
        // 重新给aspectBeanNames 赋值
        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;
}

这里大概分为三种情况:


如果List aspectNames为null,则进行初始化;

如果List aspectNames为空,则直接返回空列表;

否则则尝试从advisorsCache或者advisorFactory获取advisors

那么在第一次是一定为null的,这里会从容器中获取到(标注了Aspect注解 且未被ajc编译的bean),如我们自己定义的logAspect。最终会通过this.advisorFactory.getAdvisors(factory)得到Advisor。


那么这里List classAdvisors = this.advisorFactory.getAdvisors(factory);就是核心部分。

③ ReflectiveAspectJAdvisorFactory.getAdvisors

@Override
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
 // 得到切片类型与名称
  Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
  String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
  //进行校验 PerClauseKind.PERCFLOW PerClauseKind.PERCFLOWBELOW 是否有注解Aspect
  validate(aspectClass);
  // We need to wrap the MetadataAwareAspectInstanceFactory with a decorator
  // so that it will only instantiate once.
  // 进行包装,使其只能实例化一次
  MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
      new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);
  List<Advisor> advisors = new ArrayList<>();
  // 得到AdvisorMethod,即不包含Pointcut的方法
  for (Method method : getAdvisorMethods(aspectClass)) {
    // 得到Advisor 
    Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
    if (advisor != null) {
      advisors.add(advisor);
    }
  }
  // If it's a per target aspect, emit the dummy instantiating aspect.
// PerClauseKind.PERTARGET PerClauseKind.PERTHIS PerClauseKind.PERTYPEWITHIN才会进入
  if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
    Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
    advisors.add(0, instantiationAdvisor);
  }
  // Find introduction fields.
  // 引入通知中,尝试将每一个Field 包装为一个Advisor 
  for (Field field : aspectClass.getDeclaredFields()) {
  // 这里针对的是DeclareParents 会创建DeclareParentsAdvisor
    Advisor advisor = getDeclareParentsAdvisor(field);
    if (advisor != null) {
      advisors.add(advisor);
    }
  }
  return advisors;
}

如上代码所示,这里首先获取切面类中未标注Pointcut注解的方法,然后实例化得到InstantiationModelAwarePointcutAdvisorImpl这样一个Advisor。


然后判断aspect是否需要懒加载,如果是则实例化SyntheticInstantiationAdvisor放入advisors的第一个位置。


最后对引入通知进行处理,拿到那些标注了DeclareParents 注解的filed,得到相应的DeclareParentsAdvisor放入advisors。


至此就完成了advisors 的查找。我们可以跟踪看一下getAdvisor和getDeclareParentsAdvisor方法。

getAdvisor

如下所示,首先进行校验然后获取AspectJExpressionPointcut ,如果不为null则实例化InstantiationModelAwarePointcutAdvisorImpl。

@Nullable
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
    int declarationOrderInAspect, String aspectName) {
  validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
  AspectJExpressionPointcut expressionPointcut = getPointcut(
      candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
  if (expressionPointcut == null) {
    return null;
  }
  return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
      this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}

在实例化InstantiationModelAwarePointcutAdvisorImpl,还会对Advice进行实例化。如下所示在ReflectiveAspectJAdvisorFactory的方法中会拿到方法的AspectJAnnotation,根据其AnnotationType分别创建Advice。

// ReflectiveAspectJAdvisorFactory#getAdvice
@Override
@Nullable
public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
    MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
  Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
  validate(candidateAspectClass);
// 获取方法上的注解
  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
  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;
// 根据注解的类型创建对应的Advice
  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;
}
AnnotationType Advice
AtPointcut null
AtAround AspectJAroundAdvice
AtBefore AspectJMethodBeforeAdvice
AtAfter AspectJAfterAdvice
AtAfterReturning AspectJAfterReturningAdvice
AtAfterThrowing AspectJAfterThrowingAdvice

到这里,我们自定义的logAspect如何成为一个Advisor就清晰了。我们再看一下getDeclareParentsAdvisor。


getDeclareParentsAdvisor

这里主要为引入通知解析Advisor 。

@Nullable
private Advisor getDeclareParentsAdvisor(Field introductionField) {
// 判断DeclareParents注解
  DeclareParents declareParents = introductionField.getAnnotation(DeclareParents.class);
  if (declareParents == null) {
    // Not an introduction field
    return null;
  }
// 判断默认实现
  if (DeclareParents.class == declareParents.defaultImpl()) {
    throw new IllegalStateException("'defaultImpl' attribute must be set on DeclareParents");
  }
// 实例化DeclareParentsAdvisor
  return new DeclareParentsAdvisor(
      introductionField.getType(), declareParents.value(), declareParents.defaultImpl());
}

【3】如果同时有引入通知呢?

如下所示在切面中添加了引入通知:

@Aspect
@Component
public class LogAspect {
    @DeclareParents(value = "com.recommend.controller.HomeController",defaultImpl=MyDeclareImpl.class)
    private MyDeclareService myDeclareService;
    //...
}    

此时我们获取得到排序的eligibleAdvisors如下所示,DeclareParentsAdvisor排在了第二位。

0 = {ExposeInvocationInterceptor$1@7173} "org.springframework.aop.interceptor.ExposeInvocationInterceptor.ADVISOR"
1 = {DeclareParentsAdvisor@7137} 
2 = {InstantiationModelAwarePointcutAdvisorImpl@7136} "InstantiationModelAwarePointcutAdvisor: expression [logPointCut()]; advice method [public void com.recommend.config.LogAspect.AfterThrowing(org.aspectj.lang.JoinPoint,java.lang.Exception)]; perClauseKind=SINGLETON"
3 = {InstantiationModelAwarePointcutAdvisorImpl@7135} "InstantiationModelAwarePointcutAdvisor: expression [logPointCut()]; advice method [public void com.recommend.config.LogAspect.AfterReturning(org.aspectj.lang.JoinPoint,java.lang.Object)]; perClauseKind=SINGLETON"
4 = {InstantiationModelAwarePointcutAdvisorImpl@7134} "InstantiationModelAwarePointcutAdvisor: expression [logPointCut()]; advice method [public void com.recommend.config.LogAspect.afterMethod(org.aspectj.lang.JoinPoint)]; perClauseKind=SINGLETON"
5 = {InstantiationModelAwarePointcutAdvisorImpl@7132} "InstantiationModelAwarePointcutAdvisor: expression [logPointCut()]; advice method [public java.lang.Object com.recommend.config.LogAspect.around(org.aspectj.lang.ProceedingJoinPoint) throws java.lang.Throwable]; perClauseKind=SINGLETON"
6 = {InstantiationModelAwarePointcutAdvisorImpl@7133} "InstantiationModelAwarePointcutAdvisor: expression [execution(* com.recommend.controller.*.*(..))]; advice method [public void com.recommend.config.LogAspect.beforeMethod(org.aspectj.lang.JoinPoint)]; perClauseKind=SINGLETON"

DeclareParentsAdvisor对象如下所示:

目录
相关文章
|
4天前
|
运维 Java 程序员
Spring5深入浅出篇:基于注解实现的AOP
# Spring5 AOP 深入理解:注解实现 本文介绍了基于注解的AOP编程步骤,包括原始对象、额外功能、切点和组装切面。步骤1-3旨在构建切面,与传统AOP相似。示例代码展示了如何使用`@Around`定义切面和执行逻辑。配置中,通过`@Aspect`和`@Around`注解定义切点,并在Spring配置中启用AOP自动代理。 进一步讨论了切点复用,避免重复代码以提高代码维护性。通过`@Pointcut`定义通用切点表达式,然后在多个通知中引用。此外,解释了AOP底层实现的两种动态代理方式:JDK动态代理和Cglib字节码增强,默认使用JDK,可通过配置切换到Cglib
|
4天前
|
XML Java 数据格式
Spring使用AOP 的其他方式
Spring使用AOP 的其他方式
14 2
|
4天前
|
XML Java 数据格式
Spring 项目如何使用AOP
Spring 项目如何使用AOP
18 2
|
10天前
|
Java 开发者 Spring
Spring AOP的切点是通过使用AspectJ的切点表达式语言来定义的。
【5月更文挑战第1天】Spring AOP的切点是通过使用AspectJ的切点表达式语言来定义的。
22 5
|
10天前
|
XML Java 数据格式
Spring AOP
【5月更文挑战第1天】Spring AOP
25 5
|
10天前
|
Java 编译器 开发者
Spring的AOP理解
Spring的AOP理解
|
10天前
|
XML Java 数据格式
如何在Spring AOP中定义和应用通知?
【4月更文挑战第30天】如何在Spring AOP中定义和应用通知?
16 0
|
10天前
|
安全 Java 开发者
在Spring框架中,IoC和AOP是如何实现的?
【4月更文挑战第30天】在Spring框架中,IoC和AOP是如何实现的?
21 0
|
11天前
|
缓存 监控 Java
【Spring系列笔记】AOP
面向切面编程就是面向特定方法编程。通过将横切关注点(cross-cutting concerns)从主要业务逻辑中分离出来,提供一种更好的代码模块化和可维护性。 横切关注点指的是在应用程序中横跨多个模块或层的功能,例如日志记录、事务管理、安全性、缓存、异常处理等。
24 0
|
16天前
|
Java Spring
Spring AOP
Spring AOP
15 0