likes
comments
collection
share

你必须懂也可以懂的@Transactional原理

作者站长头像
站长
· 阅读数 11

1.前言

系统应用开发过程中通常需要使用事务来保证业务数据的一致性,实现方式如:开启事务、执行数据库写操作、提交或者回滚事务,这种标准实现方式适用于少量一致性业务,如果存在大量需要保证数据一致性的业务,不仅会让开发人员重复编码,还会给系统造成冗余代码。基于这些问题,伟大的Spring框架为我们提供了@Transactional注解,那么它是如何使用一个注解就解决了我们的烦恼呢?我们该如何着手进行分析呢?

SpringBoot集成的功能往往要从一个xxxAutoConfiguration开始说起

2.自动配置

打开TransactionAutoConfiguration自动配置类可以看到一个比较重要的注解@EnableTransactionManagement用于开启事务管理功能,@EnableTransactionManagement注解又导入了AutoProxyRegistrarProxyTransactionManagementConfiguration

2.1 事务配置

ProxyTransactionManagementConfiguration中声明了一个切面BeanFactoryTransactionAttributeSourceAdvisor,看到切面必定会有相对应的切点TransactionAttributeSourcePointcut(用于声明切入的范围)和通知TransactionInterceptor(用于实现切入目标的后续操作)。

2.2 声明@Transactional注解处理器

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

AnnotationTransactionAttributeSource实例化指定了注解解析器为SpringTransactionAnnotationParser

你必须懂也可以懂的@Transactional原理

可以看到该解析器主要用来处理@Transactional注解

2.3 注入自动代理注册器

2.自动配置提到@EnableTransactionManagement还引入了AutoProxyRegistrar,向IOC容器中注入InfrastructureAdvisorAutoProxyCreator

@Nullable
public static BeanDefinition registerAutoProxyCreatorIfNecessary(
      BeanDefinitionRegistry registry, @Nullable Object source) {
   return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);
}

InfrastructureAdvisorAutoProxyCreator实现了BeanPostProcessor接口,具有拦截并处理Bean的能力

2.3.1 Bean后置处理

@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (this.earlyProxyReferences.remove(cacheKey) != bean) {
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

2.3.2 从容器中获取所有的Advisor

/**
  * Find all candidate Advisors to use in auto-proxying.
  * @return the List of candidate Advisors
  */
protected List<Advisor> findCandidateAdvisors() {
    Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available");
    return this.advisorRetrievalHelper.findAdvisorBeans();
}

2.3.3 筛选出符合条件的Advisor

public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
    // 1.获取切点对应的MethodMatcher
    MethodMatcher methodMatcher = pc.getMethodMatcher();
    for (Class<?> clazz : classes) {
        // 2.获取当前类中的所有方法
        Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
        for (Method method : methods) {
            // 3.判断是否符合切点要求,此处的methodMatcher为TransactionAttributeSourcePointcut
            if (methodMatcher.matches(method, targetClass)) {
                return true;
            }
        }
    }

    return false;
}

你必须懂也可以懂的@Transactional原理

判断方法上是否有@Transactional注解,如果有则使用SpringTransactionAnnotationParser进行解析并生成TransactionAttribute

2.3.4 Advisor排序

sortAdvisors(eligibleAdvisors);

2.3.4 小结

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    // 1.从容器中获取所有类型为Advisor的切面
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    // 2.筛选出符合条件的切面(也就是类或方法上被@Transactional注解标注)
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    extendAdvisors(eligibleAdvisors);
    if (!eligibleAdvisors.isEmpty()) {
        // 3.对符合条件的切面进行升序排序
        eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    return eligibleAdvisors;
}

2.4 选择代理方法

@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    if (!IN_NATIVE_IMAGE &&
        (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {
        Class<?> targetClass = config.getTargetClass();
        // 1.如果实现接口则选择jdk代理
        if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
            return new JdkDynamicAopProxy(config);
        }
        // 2.选择cglib代理
        return new ObjenesisCglibAopProxy(config);
    }
    else {
        return new JdkDynamicAopProxy(config);
    }
}

2.4.1 生成代理

此处以cglib为例

@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
    try {
        Class<?> rootClass = this.advised.getTargetClass();
        Class<?> proxySuperClass = rootClass;

        // Configure CGLIB Enhancer...
        Enhancer enhancer = createEnhancer();
        if (classLoader != null) {
            enhancer.setClassLoader(classLoader);
        }
        enhancer.setSuperclass(proxySuperClass);
        enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));

        Callback[] callbacks = getCallbacks(rootClass);
    }
}
private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
    // Choose an "aop" interceptor (used for AOP calls).
    Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
}

创建Enhancer并指定回调为DynamicAdvisedInterceptor

2.5 调用代理

执行被代理对象目标方法userService.saveUser(user);时会调用DynamicAdvisedInterceptorintercept()方法

2.5.1 筛选满足条件的Advice

@Override
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
    Advised config, Method method, @Nullable Class<?> targetClass) {
    // 1.遍历所有满足条件的Advisor,也就是2.3.3章节返回的Advisor
    for (Advisor advisor : advisors) {
        if (advisor instanceof PointcutAdvisor) {
            // Add it conditionally.
            PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
            if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
                MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
                // 2. 判断是否满足切点要求
                boolean match = mm.matches(method, actualClass);
                // 3.满足切点要求
                if (match) {
                    // 3. 获取切面对应的通知,也就是TransactionInterceptor
                    MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
                    interceptorList.addAll(Arrays.asList(interceptors));
                }
            }
        }
    }
	// 4.返回满足条件的通知
    return interceptorList;
}

2.5.2 执行满足条件的Advice

@Override
@Nullable
public Object proceed() throws Throwable {
	// 1.如果没有advice可以执行,则执行目标方法
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        return invokeJoinpoint();
    }

    // 2.从advice列表中取出一个advice
    Object interceptorOrInterceptionAdvice =
        this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    // 3.执行advice的invoke方法,也就是TransactionInterceptor的invoke
	return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}

3. 事务调用

3.1 获取事务配置属性

TransactionAttributeSource tas = getTransactionAttributeSource();
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);

也就是@Transactional注解声明的属性

3.2 获取事务管理器

final TransactionManager tm = determineTransactionManager(txAttr);

从容器中获取DataSourceTransactionManagerAutoConfiguration自动配置类中声明的事务管理器JdbcTransactionManager

public class DataSourceTransactionManagerAutoConfiguration {
    @Bean
    @ConditionalOnMissingBean(TransactionManager.class)
    JdbcTransactionManager transactionManager(DataSource dataSource,
                                              ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
        JdbcTransactionManager transactionManager = new JdbcTransactionManager(dataSource);
        transactionManagerCustomizers.ifAvailable((customizers) -> customizers.customize(transactionManager));
        return transactionManager;
    }
}

3.3 执行事务

你必须懂也可以懂的@Transactional原理

4. 总结

@Transactional实现原理三要素切面切点通知

  • InfrastructureAdvisorAutoProxyCreator后置处理器拦截所有Bean
  • 遍历所有类型为Advisor切面
  • 返回满足切点条件的切面列表
  • 选择代理方法
  • 生成代理
  • 调用通知invoke()方法
    • 开启事务
    • 调用其它通知invoke()方法,如果没有执行目标方法
    • 执行异常,回滚事务
    • 执行成功,提交事务
  • 执行目标方法

了解@Transactional注解实现原理,不仅可以让我们对切面切点通知有一个清晰的认识,还可以让我们通过其思想实现类似功能,如@Cache注解实现应用缓存,@Async注解实现业务异步执行