@Transaction和自定义切面的执行顺序思考

场景

平时使用切面去加分布式锁,是先开启事务还是先尝试获得锁?这两者有啥区别?

  • 先获取锁后执行事务 正确姿势
  • 先获取事务,再获取锁,再释放锁,最后提交事务
    1. 这种情况线程不安全,当线程a释放锁,但还未提交事务,线程b此刻拿到锁并抢在线程a之前提交事务,这样就会导致修改覆盖
    2. 如果分布式锁做一些限流措施,也会产生一定的性能损失,不应该每次请求都开启事务后再进行准入判断

@Transaction分析

  1. @EnableTransactionManagement注解中可看到order属性且默认为最低优先级,最后开启事务,最早提交事务
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Import(TransactionManagementConfigurationSelector.class)
    public @interface EnableTransactionManagement {
    
    	boolean proxyTargetClass() default false;
    
    	AdviceMode mode() default AdviceMode.PROXY;
    
    	int order() default Ordered.LOWEST_PRECEDENCE; 
    }
  1. 通过TransactionManagementConfigurationSelector查看Aop实现的2种方式PROXY和ASPECTJ
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
	/**
	 * {@inheritDoc}
	 * @return {@link ProxyTransactionManagementConfiguration} or
	 * {@code AspectJTransactionManagementConfiguration} for {@code PROXY} and
	 * {@code ASPECTJ} values of {@link EnableTransactionManagement#mode()}, respectively
	 */
	@Override
	protected String[] selectImports(AdviceMode adviceMode) {
		switch (adviceMode) {
			case PROXY:
				return new String[] {AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()};
			case ASPECTJ:
				return new String[] {TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME};
			default:
				return null;
		}
	}

}
  1. 查看PROXY配置类,发现从enableTx(即从@EnableTransactionManagement设置的order值)获取order值设置到advisor的order中(Aop会根据advisor的order从小到大进行排序)
@Configuration
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {

	@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {
		BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
		advisor.setTransactionAttributeSource(transactionAttributeSource());
		advisor.setAdvice(transactionInterceptor());
		advisor.setOrder(this.enableTx.<Integer>getNumber("order")); // 设置@Transactional注解Advisor的order
		return advisor;
	}
}
  1. 查看AspectJ配置类, 查看AnnotationTransactionAspect属于AspectJ语法,AspectJ支持的是静态代理,支持以下三种方式编译期织入、编译期织入 和 加载期织入,当设置启动参数-javaagent:D:\repository\org\aspectj\aspectjweaver\1.8.9\aspectjweaver-1.8.9.jar和事务注解中设置mode=AspectJ方式是通过加载时织入代码,此时包含有@Transactional的public的类和方法在JVM加载类时被修改class静态织入增强的代码,属于最后执行开启事务和最先执行提交事务,即优先级为最低
    Aspect加载时织入
    AspectJ在Spring中的使用
@Configuration
public class AspectJTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {

	@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ASPECT_BEAN_NAME)
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public AnnotationTransactionAspect transactionAspect() {
		AnnotationTransactionAspect txAspect = AnnotationTransactionAspect.aspectOf();
		if (this.txManager != null) {
			txAspect.setTransactionManager(this.txManager);
		}
		return txAspect;
	}

}

自定义切面分析

  1. @Aspect注解类由方法ReflectiveAspectJAdvisorFactory#getAdvisors最终会解析成InstantiationModelAwarePointcutAdvisorImpl implement Advisor(具体的原理可参考上篇关于SpringAop源码实现文章分析),其中的getOrder()方法是由this.aspectInstanceFactory.getOrder()实现,逻辑分析下面代码
 // ReflectiveAspectJAdvisorFactory#getAdvisors
 @Override
	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);
	}
 // this.aspectInstanceFactory.getOrder()
 @Override
	public int getOrder() {
        // 当前的实例是否已经注册到Spring容器
		Class<?> type = this.beanFactory.getType(this.name);
		if (type != null) {
			if (Ordered.class.isAssignableFrom(type) && this.beanFactory.isSingleton(this.name)) {
                // 如果是单例对象且是Ordered的实现类,则执行getOrder()方法,具体的值由实现类实现
				return ((Ordered) this.beanFactory.getBean(this.name)).getOrder();
			}
            // 如果不是Ordered的实现类,从类上的@Order @Priority注解获取order值,如果没有则默认为最低优先级
			return OrderUtils.getOrder(type, Ordered.LOWEST_PRECEDENCE);
		}
        // 默认返回最低优先级
		return Ordered.LOWEST_PRECEDENCE;
	}

通过上述分析发现,如果使用@Aspect注解没有指定顺序则为最低的优先级
2. 自定义类实现Advisor,如BeanFactoryTransactionAttributeSourceAdvisor是SpringTransaction的Advisor实现,关于顺序则自己进行实现,如未实现,最终springaop在获取到所有advisors后进行排序,排序类org.springframework.core.annotation.AnnotationAwareOrderComparator 可发现如果没有实现则按照最低优先级处理。
3. 综上自定义切面不管使用何种方式实现如未指定order值,则按最低优先级处理

顺序核心代码分析

通过上述对自定义,@Aspect以及AspectJ静态织入的分析,继续再看获取aop执行链的顺序的核心代码

  1. 执行链的顺序源码位置org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#findEligibleAdvisors
    //org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#findEligibleAdvisors
    protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
         // 获取所有advisors逻辑:先获取所有spring advisors ,然后再根据@AspectJ的注解创建并返回所有的advisors
   	List<Advisor> candidateAdvisors = findCandidateAdvisors();
   	List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
   	extendAdvisors(eligibleAdvisors);
   	if (!eligibleAdvisors.isEmpty()) {
   		eligibleAdvisors = sortAdvisors(eligibleAdvisors); // 对Advisors先进行order值排序,如果是同一个Advisor则根据指定的注解排序,如果注解一样则根据注解对应的方法名排序(具体可看之前发过的SpringAop源码分析)
   	}
   	return eligibleAdvisors;
   }
   
   @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.
   	advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
   	return advisors;
 	}

根据上述代码可知:

  1. 采用jdk or cglib的动态代理对Advisors先进行order值排序
    1. order值相同则判断是否同一个Advisor
      1. 相同Advisor则根据指定的注解排序
        1. 注解相同:根据注解对应的方法名自然顺序排序
        2. 注解不同:按照注解排序
      2. 不同Advisor则根据默认是按照获取到Advisors的顺序排序,Spring的Advisors的顺序是大于AspectJ的aspects的顺序,此外还有相同springAdvisors和AspectJ的aspects各自内部的顺序,这部分取决于registerBeanDefinition的顺序,registerBeanDefinition顺序太过于庞杂,不过一般来说通过ComponentScan指定basepage等方式应该是按照包名的自然顺序排序(不能保证)(重点
    2. order值不同则直接按order值排序
  2. 静态代理时,@Transactional的优先级最低,其他的自定义切面的增强均优先于@Transactional

总结

  • @Transactional事务当model=PROXY时,可指定优先级order,若未指定则为最低优先级,此时注意如果项目中有其他自定义的切面未配置order指,那么@Transactional的优先级是比项目中@Aspect注解自定义的切面高,如果自定义的切面是通过实现Advisor实现的顺序取决于registerBeanDefinition的顺序
  • @Transactional事务当model=ASPECTJ时,项目引入了AspectJ的静态代理,则@Transactional执行的优先级低于基于jdk和cglib的动态代理,需要注意的是需要关注此时自己编写的Aspect的配置是否@Transactional的Aspect配置执行的优先级顺序,关于AspectJ的静态代理顺序不在本文讨论范围,有兴趣可一起讨论
  • 回到开题的分布式锁的注解问题,如果不指定order的情况下,@Transactional的执行优先于分布式锁注解,会引发线程安全问题。

你可能感兴趣的:(Spring入门,spring,Transaction,后端)