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的执行过程放在下篇中讲解

相关推荐
全靠bug跑7 分钟前
Spring Cloud OpenFeign 实战三部曲:快速集成 · 连接池优化 · 客户端抽取
java·spring boot·openfeign
北城以北88881 小时前
Spring定时任务与Spring MVC拦截器
spring boot·spring·mvc
Victor3561 小时前
Netty(20)如何实现基于Netty的WebSocket服务器?
后端
缘不易1 小时前
Springboot 整合JustAuth实现gitee授权登录
spring boot·后端·gitee
Kiri霧1 小时前
Range循环和切片
前端·后端·学习·golang
WizLC1 小时前
【Java】各种IO流知识详解
java·开发语言·后端·spring·intellij idea
Mr.朱鹏1 小时前
SQL深度分页问题案例实战
java·数据库·spring boot·sql·spring·spring cloud·kafka
Victor3561 小时前
Netty(19)Netty的性能优化手段有哪些?
后端
爬山算法1 小时前
Netty(15)Netty的线程模型是什么?它有哪些线程池类型?
java·后端
白宇横流学长2 小时前
基于SpringBoot实现的冬奥会科普平台设计与实现【源码+文档】
java·spring boot·后端