Spring AOP以及事务详解(一)

书接上回

上一章,主要介绍了 Spring 通过使用SqlSessionTemplate 这个 SqlSession的门面类,在内部通过动态代理的方式,在创建 真正执行所需的 DefaultSqlSession(Mybatis原生组件) 时,会通过Spring的 事务管理器 *TransactionSynchronizationManager 来获取 ThrealLocal 中 SqlSessionFactory 为key 的 SqlSessionHolder ,同时在 事务环境中 ,会向 Spring 中注册 事务执行的 回调 钩子 SqlSessionSynchronization , 用于管理创建的 SqlSession 的生命周期(在事务完成之后,关闭SqlSession等)

但是这其中也缺失了 关于Spring AOP 以及事务执行管理的介绍 ,以及文章最后提出了问题

像mybatis 这种作为Spring的集成组件,也可以说是下游组件,从代码里也可以看到 相关组件的获取是从TransactionSynchronizationManager中通过ThreadLocal获取的,所以Connection的放入也是Spring先放的,所以 在事务场景下默认情况下,Spring的事务会在 AOP 一开始执行就默认创建好一个 Connection,而不管后续是否真正有使用Connection的需求。这块也会在下面的 源码 解读中会看到源码是怎么处理这块的

最后本文 主要详细梳理下下Spring 事务的执行过程,以及Spring 事务执行过程中,一些关键组件的使用

Spring 事务

Springboot的自动配置

Springboot-autoconfigure 项目中的 org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration 配置类如下,可以看到是通过静态类的方式默认引入了 CglibAutoProxyConfiguration 配置,而这个配置又被 @EnableTransactionManagement(proxyTargetClass = true) 注解标注,所以可以看到Springboot其实还是利用了Spring时期的@EnableXXX注解。下面详细讲解下 @EnableTransactionManagement(proxyTargetClass = true) 注解

less 复制代码
 @AutoConfiguration
@ConditionalOnClass(PlatformTransactionManager.class)
@EnableConfigurationProperties(TransactionProperties.class)
public  class  TransactionAutoConfiguration {
    //............ 省略 .................
    
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnBean(TransactionManager.class)
    @ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class)
    public  static  class  EnableTransactionManagementConfiguration {

       @Configuration(proxyBeanMethods = false)
       @EnableTransactionManagement(proxyTargetClass = false)
       @ConditionalOnProperty(prefix = "spring.aop" , name = "proxy-target-class" , havingValue = "false" )
       public  static  class  JdkDynamicAutoProxyConfiguration {

       }

       @Configuration(proxyBeanMethods = false)
       @EnableTransactionManagement(proxyTargetClass = true)
       @ConditionalOnProperty(prefix = "spring.aop" , name = "proxy-target-class" , havingValue = "true" ,
             matchIfMissing = true)
       public  static  class  CglibAutoProxyConfiguration {

       }

    }
    
    //............ 省略 .................

}

@EnableTransactionManagement(proxyTargetClass = true)

而这个注解最终是通过 @Import (TransactionManagementConfigurationSelector .class) 引入一个实现了

ImportSelector 接口的配置导入类

less 复制代码
 @Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
 @Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {

    /**
     * Indicate whether subclass-based (CGLIB) proxies are to be created ({ @code true}) as
     * opposed to standard Java interface-based proxies ({ @code false}). The default is
     * { @code false}. <strong> Applicable only if { @link #mode()} is set to
     * { @link AdviceMode#PROXY} </strong> .
     * <p> Note that setting this attribute to { @code true} will affect <em> all </em>
* Spring-managed beans requiring proxying, not just those marked with
     * { @code @Transactional}. For example, other beans marked with Spring's
     * { @code @Async} annotation will be upgraded to subclass proxying at the same
     * time. This approach has no negative impact in practice unless one is explicitly
     * expecting one type of proxy vs another, e.g. in tests.
     */
    boolean proxyTargetClass() default  false;

    /**
     * Indicate how transactional advice should be applied.
     * <p><b> The default is { @link AdviceMode#PROXY}. </b>
* Please note that proxy mode allows for interception of calls through the proxy
     * only. Local calls within the same class cannot get intercepted that way; an
     * { @link Transactional} annotation on such a method within a local call will be
     * ignored since Spring's interceptor does not even kick in for such a runtime
     * scenario. For a more advanced mode of interception, consider switching this to
     * { @link AdviceMode#ASPECTJ}.
     */
    AdviceMode mode() default AdviceMode.PROXY;

    /**
     * Indicate the ordering of the execution of the transaction advisor
     * when multiple advices are applied at a specific joinpoint.
     * <p> The default is { @link Ordered#LOWEST_PRECEDENCE}.
     */
    int order() default Ordered.LOWEST_PRECEDENCE;

}

TransactionManagementConfigurationSelector

typescript 复制代码
 @Override
protected  String[] selectImports(AdviceMode adviceMode) {
    switch (adviceMode) {
       case  PROXY:
          return new String[] {AutoProxyRegistrar.class.getName(),
                ProxyTransactionManagementConfiguration.class.getName()};
       case  ASPECTJ:
          return  new String[] {determineTransactionAspectClass()};
       default:
          return  null;
    }
}

可以看到注入了两个bean ,

一个是 AutoProxyRegistrar , 这个类主要向容器中注入 InfrastructureAdvisorAutoProxyCreator 这个用于处理 AOP 的BeanPostProcesser ,而一般情况下由于我们系统会添加 AspectJ 风格的注解 @EnableAspectJAutoProxy ,所以系统中最终用于处理AOP的BeanPostProcessor最终是 AnnotationAwareAspectJAutoProxyCreator ,而这个类也是一个BeanPostProcessor,它会在Bean创建时的初始化阶段对Bean的实例进行切面逻辑,这个类也非常重要,会在下面的解析中再次出现

二是向容器中注入配置类 ProxyTransactionManagementConfiguration, 这个配置类如下

java 复制代码
 @Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public  class  ProxyTransactionManagementConfiguration  extends  AbstractTransactionManagementConfiguration {

    @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(
          TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) {

       BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
       advisor.setTransactionAttributeSource(transactionAttributeSource);
       advisor.setAdvice(transactionInterceptor);
       if (this.enableTx != null) {
          advisor.setOrder(this.enableTx.<Integer>getNumber( "order" ));
       }
       return advisor;
    }

    @Bean
 @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public TransactionAttributeSource transactionAttributeSource() {
       return  new AnnotationTransactionAttributeSource();
    }

    @Bean
 @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {
       TransactionInterceptor interceptor = new TransactionInterceptor();
       interceptor.setTransactionAttributeSource(transactionAttributeSource);
       if (this.txManager != null) {
          interceptor.setTransactionManager(this.txManager);
       }
       return interceptor;
    }

}

可以看到其实主要是向容器中注入了一个 Advisor (一个Advice+PointCut) :

Advisor: BeanFactoryTransactionAttributeSourceAdvisor

可以看到向这个Advisor中设置了 transactionAttributeSource- 这个类用于解析方法上或者类上关于@Transactional注解原信息,稍微看下注入的这个类的实现类

AnnotationTransactionAttributeSource

csharp 复制代码
public  AnnotationTransactionAttributeSource(boolean  publicMethodsOnly) {
    this.publicMethodsOnly = publicMethodsOnly;
    if (jta12Present || ejb3Present) {
       this.annotationParsers = new LinkedHashSet<>(4);
       this.annotationParsers.add(new SpringTransactionAnnotationParser());
       if (jta12Present) {
          this.annotationParsers.add(new JtaTransactionAnnotationParser());
       }
       if (ejb3Present) {
          this.annotationParsers.add(new Ejb3TransactionAnnotationParser());
       }
    }
    else {
       this.annotationParsers = Collections.singleton(new SpringTransactionAnnotationParser());
    }
}

可以看到这个类主要是集成了一个 SpringTransactionAnnotationParser 注解解析器,用于处理 @Transactional 注解

另一个就是Advice - TransactionInterceptor 而这个 interceptor 其实就是一个Advice , 而且是一个环绕通知,其实也可以理解,事务本身就是要在一个被包裹的方法中执行,在事务开始前,获取Connection, 并设置commit=false,在事务结束的时候调用commit 或者 rollback. 而这个类也将 使 后面分析的重点,几乎所有的关于事务的逻辑都在这个Advice中。看下这个类的签名,是一个Advice也是一个MethodInterceptor

事务AOP的处理

众所周知,Spring容器在启动的后期,也就是BeanDefinition全都加载完成之后,会进行单例Bean的创建。而Bean的创建会经历 "实例化" ,"属性注入" ,"初始化" 阶段,而众多的 BeanPostProcesser 就会在这个阶段对创建好的Bean做最后的加工,而作为处理 AspectJ 风格的(使用 @Aspect 的切面),以及 @Transactional 注解的方法和类的 BeanPostProcessor 的实现 AnnotationAwareAspectJAutoProxyCreator ,则在其中发挥着重要的作用

AnnotationAwareAspectJAutoProxyCreator

其继承关系图如下:其中比较重要的 AbstractAutoProxyCreator ,这个类是一个抽象模板类,实现了切面扫描注册, 以及后置的Bean的动态代理生成的逻辑,下面详细分析下这个类

由于其BeanPostProcessor的身份,所以在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

ini 复制代码
protected  Object doCreateBean(String  beanName, RootBeanDefinition  mbd, @Nullable  Object[] args)
       throws BeanCreationException {

    // Instantiate the bean.
    BeanWrapper instanceWrapper = null;
    if (mbd.isSingleton()) {
       instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
    }
    if (instanceWrapper == null) {
       instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
    Object bean = instanceWrapper.getWrappedInstance();
    Class<?> beanType = instanceWrapper.getWrappedClass();
    if (beanType != NullBean.class) {
       mbd.resolvedTargetType = beanType;
    }
    
    //............. 省略若干行代码...............

    // Initialize the bean instance.
    Object exposedObject = bean;
    try {
       populateBean(beanName, mbd, instanceWrapper);
       //在这里会进行Bean的初始化,初始化流程中会执行BeanPostProcessor的前置逻辑和后置逻辑处理
       exposedObject = initializeBean(beanName, exposedObject, mbd);
    }
    catch (Throwable  ex) {
       if (ex instanceof  BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
          throw (BeanCreationException) ex;
       }
       else {
          throw  new BeanCreationException(
                mbd.getResourceDescription(), beanName, "Initialization of bean failed" , ex);
       }
    }

    //............. 省略若干行代码...............

    return exposedObject;
}

initializeBean

而在 initializeBean 方法中会执行BeanPostProcessor的前置处理逻辑和后置处理逻辑,而 Advisor的扫描与识别以及组装加载则都是在这个前置处理逻辑中进行的。而后置方法则主要是根据

scss 复制代码
protected  Object initializeBean(String  beanName, Object  bean, @Nullable RootBeanDefinition mbd) {
    if (System.getSecurityManager() != null) {
       AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
          invokeAwareMethods(beanName, bean);
          return  null;
       }, getAccessControlContext());
    }
    else {
       invokeAwareMethods(beanName, bean);
    }

    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
       wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }

    try {
       invokeInitMethods(beanName, wrappedBean, mbd);
    }
    catch (Throwable  ex) {
       throw  new BeanCreationException(
             (mbd != null ? mbd.getResourceDescription() : null),
             beanName, "Invocation of init method failed" , ex);
    }
    if (mbd == null || !mbd.isSynthetic()) {
       wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }

    return wrappedBean;
}
applyBeanPostProcessorsBeforeInitialization

会走到 org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessBeforeInstantiation中

kotlin 复制代码
 @Override
public  Object postProcessBeforeInstantiation(Class<?> beanClass, String  beanName) {
    Object cacheKey = getCacheKey(beanClass, beanName);

    if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
       if (this.advisedBeans.containsKey(cacheKey)) {
          return  null;
       }
       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);
       }
       Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
       Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
       this.proxyTypes.put(cacheKey, proxy.getClass());
       return proxy;
    }

    return  null;
}

并最终在shouldSkip方法中完成了 Advisor 的扫描 。 一个Advisor = Advice + PointCut . 下面在详细介绍下是如何进行扫描的

typescript 复制代码
 @Override
protected  boolean shouldSkip(Class<?> beanClass, String  beanName) {
    // TODO: Consider optimization by caching the list of the aspect names
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    for (Advisor advisor : candidateAdvisors) {
       if (advisor instanceof  AspectJPointcutAdvisor &&
             ((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {
          return  true;
       }
    }
    return  super.shouldSkip(beanClass, beanName);
}

接着在 findCandidateAdvisors() 方法中真正进行Advisor的查找

kotlin 复制代码
 @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;
}

说明一下:List <Advisor > advisors = super .findCandidateAdvisors(); 这个类的职责主要是查找spring 内部实现的Advisor . 而 this.aspectJAdvisorsBuilder.buildAspectJAdvisors() 主要是查找AspectJ风格的Advisor,下面详细看下

两个方法

super .findCandidateAdvisors() ,最终来到org.springframework.aop.framework.autoproxy.BeanFactoryAdvisorRetrievalHelper#findAdvisorBeans 方法,可以看到其实是先从容器中获取Advisor类型的Bean的名称 - org.springframework.transaction.config.internalTransactionAdvisor , 而这个正是前面ProxyTransactionManagementConfiguration 配置中的BeanFactoryTransactionAttributeSourceAdvisor , 剩下的就是通过beanFactory.getBean(name, Advisor.class) 获取Bean实例了

kotlin 复制代码
public  List<Advisor> findAdvisorBeans() {
    // Determine list of advisor bean names, if not cached already.
    String[] advisorNames = this.cachedAdvisorBeanNames;
    if (advisorNames == null) {
        // 这里查找所有Advisor类型的Bean的名称
       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) {
       //这里isEligibleBean 默认返回 True
       if (isEligibleBean(name)) {
          if (this.beanFactory.isCurrentlyInCreation(name)) {
             if (logger.isTraceEnabled()) {
                logger.trace( "Skipping currently created advisor '" + name + "'" );
             }
          }
          else {
             try {
                //这里是主要逻辑, 通过BeanFactory.getBean的方式获取指定name的bean实例
                advisors.add(this.beanFactory.getBean(name, Advisor.class));
             }
             // ........ 省略一些异常处理......
          }
       }
    }
    return advisors;
}

解析完 Spring 内置的 Advisor 之后,开始解析用户自定义 AspectJ 风格的Advisor,也就是我们经常在代码中使用@Aspect 编写的切面逻辑了,这块的主要解析代码在 this.aspectJAdvisorsBuilder.buildAspectJAdvisors() 中,下面简单看下这个方法实现 , 在关键处添加了相关注释,并且省略了一些不相干的代码

kotlin 复制代码
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,然后逐个遍历解析是否是 @Aspect 标注的类
             String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                   this.beanFactory, Object.class, true, false);
             for (String beanName : beanNames) {
                if (!isEligibleBean(beanName)) {
                   continue;
                }
                //然后获取这个Bean的Class类型
                Class<?> beanType = this.beanFactory.getType(beanName, false);
                if (beanType == null) {
                   continue;
                }
                //这里判断Class上是否有 @Aspect 注解且不是compiledByAj编译的类
                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);
                      if (this.beanFactory.isSingleton(beanName)) {
                         this.advisorsCache.put(beanName, classAdvisors);
                      }
                      else {
                         this.aspectFactoryCache.put(beanName, factory);
                      }
                      advisors.addAll(classAdvisors);
                   }
                   else {
                      //........ 省略若干代码............
                   }
                }
             }
             this.aspectBeanNames = aspectNames;
             return advisors;
          }
       }
    }
    //........ 省略若干代码............
    return advisors;
}

主要看下真正进行解析的地方

List classAdvisors = this.advisorFactory.getAdvisors(factory);

scss 复制代码
@Override
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
    Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
    String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
    validate(aspectClass);
MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
          new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);

    List<Advisor> advisors = new ArrayList<>();
    //这里通过 getAdvisorMethods 方法获取Class中所有非 @PointCut注解标注的方法,
    //同时这里方法里指定了切面中通知的顺序, Around->before->after->afterReturn->afterThrowing,以及相同类型的字典的排序
    for (Method method : getAdvisorMethods(aspectClass)) {
       //这里就是根据方法组装成Advisor的地方
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);
       if (advisor != null) {
          advisors.add(advisor);
       }
    }
    //...........................省略若干代码.............
    return advisors;
}

下面详细看下Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);是如何解析并组装成Advisor的,可以看到最终是构造了一个InstantiationModelAwarePointcutAdvisorImpl实例返回了,所以也可以看到切面中每一个通知方法都是一个Advisor。 下面进入InstantiationModelAwarePointcutAdvisorImpl构造函数中详细看下做了什么

kotlin 复制代码
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
       int declarationOrderInAspect, String aspectName) { 
       
    AspectJExpressionPointcut expressionPointcut = getPointcut(
          candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
    if (expressionPointcut == null) {
       return null;
    }

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

public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut,
       Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory,
       MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {

    this.declaredPointcut = declaredPointcut;
    this.declaringClass = aspectJAdviceMethod.getDeclaringClass();
    this.methodName = aspectJAdviceMethod.getName();
    this.parameterTypes = aspectJAdviceMethod.getParameterTypes();
    this.aspectJAdviceMethod = aspectJAdviceMethod;
    this.aspectJAdvisorFactory = aspectJAdvisorFactory;
    this.aspectInstanceFactory = aspectInstanceFactory;
    this.declarationOrder = declarationOrder;
    this.aspectName = aspectName;

    if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
       // Static part of the pointcut is a lazy type.
Pointcut preInstantiationPointcut = Pointcuts.union(
             aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut);

       // Make it dynamic: must mutate from pre-instantiation to post-instantiation state.
 // If it's not a dynamic pointcut, it may be optimized out
 // by the Spring AOP infrastructure after the first evaluation.
this.pointcut = new PerTargetInstantiationModelPointcut(
             this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory);
       this.lazy = true;
    }
    else {
       // A singleton aspect.
this.pointcut = this.declaredPointcut;
       this.lazy = false;
       this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
    }
}

其中比较重要的方法是 this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut); 并且最终会走到如下的方法里,可以看到构造了各种Advice.

ini 复制代码
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;

    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;
}
小总结

这样就把 Spring内置Advosor 和 自定义实现的Aspect 切面中的各种通知方法解析成了并缓存起来了

applyBeanPostProcessorsAfterInitialization

在before方法中完成了Advisor的识别以及组装工作之后,在after方法中开始对Bean在需要的时候生成代理了

kotlin 复制代码
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
       return bean;
    }
    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
       return bean;
    }
    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
       this.advisedBeans.put(cacheKey, Boolean.FALSE);
       return bean;
    }

    // Create proxy if we have advice.
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;
}

上面的方法思路也很清晰,再次校验一些基础设施Bean和需要跳过的Bean不需要代理,之后就是通过getAdvicesAndAdvisorsForBean方法获取能用于当前Bean的Advisor , 如果获取到Advisor不为空,则进行创建代理,否则返回原bean

getAdvicesAndAdvisorsForBean 内部调用了 findEligibleAdvisors 方法,下面详细看下这个方法,思路还是很清晰的,先查找所有的候选Advisor,然后过滤能应用在当前Bean上的Advisor,之后排序,最后返回。逐个来看

ini 复制代码
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    extendAdvisors(eligibleAdvisors);
    if (!eligibleAdvisors.isEmpty()) {
       eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    return eligibleAdvisors;
}

findCandidateAdvisors 方法 ,可以看到之前在before方法中已经分析过了,就是查找Spring内置的Advisor 和 解析 Aspect 切面方法为Advisor

kotlin 复制代码
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;
}

接着看下 findAdvisorsThatCanApply 方法,最终走到 canApply 方法中, 可以看到逻辑还是比较清晰的,根据Pointcut 的ClassFilter校验是否匹配目标class,之后再根据PointCut的方法匹配器去匹配目标class中所有的方法,看是否匹配,只要有一个方法满足就可以

ini 复制代码
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();
    if (methodMatcher == MethodMatcher.TRUE) {
       // No need to iterate the methods if we're matching any method anyway...
return true;
    }

    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) {
          if (introductionAwareMethodMatcher != null ?
                introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
                methodMatcher.matches(method, targetClass)) {
             return true;
          }
       }
    }

    return false;
}

那么接下来来看下对于Spring 内置的Advisor(BeanFactoryTransactionAttributeSourceAdvisor)来说,Pointcut的实现是TransactionAttributeSourcePointcut,而TransactionAttributeSourcePointcut的实现中,其实不管是ClassFilter 还是MethodMatcher ,最终都是通过TransactionAttributeSource解析类或者方法上有没有

typescript 复制代码
public class BeanFactoryTransactionAttributeSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {

    @Nullable
    private TransactionAttributeSource transactionAttributeSource;

    private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() {
       @Override
       @Nullable
       protected TransactionAttributeSource getTransactionAttributeSource() {
          return transactionAttributeSource;
       }
    };


    /**
* Set the transaction attribute source which is used to find transaction
* attributes. This should usually be identical to the source reference
* set on the transaction interceptor itself.
* @see TransactionInterceptor#setTransactionAttributeSource
*/
public void setTransactionAttributeSource(TransactionAttributeSource transactionAttributeSource) {
       this.transactionAttributeSource = transactionAttributeSource;
    }

    /**
* Set the { @link ClassFilter} to use for this pointcut.
* Default is { @link ClassFilter#TRUE}.
*/
public void setClassFilter(ClassFilter classFilter) {
       this.pointcut.setClassFilter(classFilter);
    }

    @Override
    public Pointcut getPointcut() {
       return this.pointcut;
    }

}
java 复制代码
abstract class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {

    protected TransactionAttributeSourcePointcut() {
       setClassFilter(new TransactionAttributeSourceClassFilter());
    }


    @Override
    public boolean matches(Method method, Class<?> targetClass) {
       TransactionAttributeSource tas = getTransactionAttributeSource();
       return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
    }

    @Override
    public boolean equals(@Nullable Object other) {
       if (this == other) {
          return true;
       }
       if (!(other instanceof TransactionAttributeSourcePointcut)) {
          return false;
       }
       TransactionAttributeSourcePointcut otherPc = (TransactionAttributeSourcePointcut) other;
       return ObjectUtils.nullSafeEquals(getTransactionAttributeSource(), otherPc.getTransactionAttributeSource());
    }

    @Override
    public int hashCode() {
       return TransactionAttributeSourcePointcut.class.hashCode();
    }

    @Override
    public String toString() {
       return getClass().getName() + ": " + getTransactionAttributeSource();
    }


    /**
* Obtain the underlying TransactionAttributeSource (may be { @code null}).
* To be implemented by subclasses.
*/
@Nullable
    protected abstract TransactionAttributeSource getTransactionAttributeSource();


    /**
* { @link ClassFilter} that delegates to { @link TransactionAttributeSource#isCandidateClass}
* for filtering classes whose methods are not worth searching to begin with.
*/
private class TransactionAttributeSourceClassFilter implements ClassFilter {

       @Override
       public boolean matches(Class<?> clazz) {
          if (TransactionalProxy.class.isAssignableFrom(clazz) ||
                TransactionManager.class.isAssignableFrom(clazz) ||
                PersistenceExceptionTranslator.class.isAssignableFrom(clazz)) {
             return false;
          }
          TransactionAttributeSource tas = getTransactionAttributeSource();
          return (tas == null || tas.isCandidateClass(clazz));
       }
    }

}

由于我们自定义的Bean(UserService) 上是有事务的注解的,以及有被自定义的aspect切面拦截,所以这里会返回4个Advosir, 见下图,第一个是Spring添加的Interceptor,第二个是用于处理事务的Interceptor ,后面两个是Aspect切面的两个前置通知方法解析而成的Advisor

小总结:
从上面的代码中可以看到,spring其实做的事情也没那么复杂,也就是找到容器中所有的候选Advosor,解析工作其实在before方法中也已经处理过一遍了。之后根据这个Advisor中的Pointcut的规则看能否应用在当前的bean上,最终把所有符合的Advisor收集成一个数组,之后就是根据这个Advisor数组来创建代理对象了。

鉴于篇幅,创建代理以及事务Interceptor的执行过程放在下篇中讲解

相关推荐
vx_dmxq2111 小时前
【微信小程序学习交流平台】(免费领源码+演示录像)|可做计算机毕设Java、Python、PHP、小程序APP、C#、爬虫大数据、单片机、文案
java·spring boot·python·mysql·微信小程序·小程序·idea
武子康1 小时前
大数据-167 ELK Elastic Stack(ELK) 实战:架构要点、索引与排错清单
大数据·后端·elasticsearch
9号达人2 小时前
优惠系统演进:从"实时结算"到"所见即所得",前端传参真的鸡肋吗?
java·后端·面试
q***07142 小时前
Spring Boot 中使用 @Transactional 注解配置事务管理
数据库·spring boot·sql
wei_shuo2 小时前
openEuler 底座赋能:openGauss 数据库部署与性能实战评测
后端
用户4098170215102 小时前
Python 的基本类型
后端
codetown2 小时前
openai-go通过SOCKS5代理调用外网大模型
人工智能·后端
星辞树2 小时前
MIT 6.824 Lab 3 通关实录:从 Raft 到高可用 KV 存储
后端
q***33373 小时前
Spring Boot项目接收前端参数的11种方式
前端·spring boot·后端