场景
平时使用切面去加分布式锁,是先开启事务还是先尝试获得锁?这两者有啥区别?
- 先获取锁后执行事务 正确姿势
- 先获取事务,再获取锁,再释放锁,最后提交事务
- 这种情况线程不安全,当线程a释放锁,但还未提交事务,线程b此刻拿到锁并抢在线程a之前提交事务,这样就会导致修改覆盖
- 如果分布式锁做一些限流措施,也会产生一定的性能损失,不应该每次请求都开启事务后再进行准入判断
@Transaction分析
- @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;
}
- 通过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;
}
}
}
- 查看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;
}
}
- 查看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;
}
}
自定义切面分析
- @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执行链的顺序的核心代码
- 执行链的顺序源码位置
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;
}
根据上述代码可知:
- 采用jdk or cglib的动态代理对Advisors先进行order值排序
- order值相同则判断是否同一个Advisor
- 相同Advisor则根据指定的注解排序
- 注解相同:根据注解对应的方法名自然顺序排序
- 注解不同:按照注解排序
- 不同Advisor则根据默认是按照获取到Advisors的顺序排序,Spring的Advisors的顺序是大于AspectJ的aspects的顺序,此外还有相同springAdvisors和AspectJ的aspects各自内部的顺序,这部分取决于registerBeanDefinition的顺序,registerBeanDefinition顺序太过于庞杂,不过一般来说通过ComponentScan指定basepage等方式应该是按照包名的自然顺序排序(不能保证)(重点)
- 相同Advisor则根据指定的注解排序
- order值不同则直接按order值排序
- order值相同则判断是否同一个Advisor
- 静态代理时,@Transactional的优先级最低,其他的自定义切面的增强均优先于@Transactional
总结
- @Transactional事务当model=PROXY时,可指定优先级order,若未指定则为最低优先级,此时注意如果项目中有其他自定义的切面未配置order指,那么@Transactional的优先级是比项目中@Aspect注解自定义的切面高,如果自定义的切面是通过实现Advisor实现的顺序取决于registerBeanDefinition的顺序
- @Transactional事务当model=ASPECTJ时,项目引入了AspectJ的静态代理,则@Transactional执行的优先级低于基于jdk和cglib的动态代理,需要注意的是需要关注此时自己编写的Aspect的配置是否@Transactional的Aspect配置执行的优先级顺序,关于AspectJ的静态代理顺序不在本文讨论范围,有兴趣可一起讨论
- 回到开题的分布式锁的注解问题,如果不指定order的情况下,@Transactional的执行优先于分布式锁注解,会引发线程安全问题。