Spring 中 Transactional 注解原理

栏目: Java · 发布时间: 5年前

内容简介:利用 Spring 框架可以很容易的使用注解的方式来使用事务,为我们的开发带来了巨大的便利,这种便利的实现是通过 Spring 本身的一系列机制来实现的,主要包含动态代理和 Spring Bean 的加载过程。本文将深入源码,揭开层层面纱…我们只需要在对应的方法上使用为何不会生效?是因为这样的调用不会经过 Spring 的代理,无法通过 Spring 的 advisor 来拦截数据库操作请求。

利用 Spring 框架可以很容易的使用注解的方式来使用事务,为我们的开发带来了巨大的便利,这种便利的实现是通过 Spring 本身的一系列机制来实现的,主要包含动态代理和 Spring Bean 的加载过程。本文将深入源码,揭开层层面纱…

Spring @Transactional 的使用

@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public void methodInTransaction() {
    // 对数据库的一些操作
}

我们只需要在对应的方法上使用 @Transactional 注解即可让这个方法在事务中执行。这里需要注意的是,如果是在一个类中的两个方法,事务是不会生效的。举例:

import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

/**
 * Created by zhangbo on 2019-02-28.
 */
public class TransactionTest {
    public void methodNotIntransaction() {
        this.methodInTransaction(); // 此时被调用的方法 methodInTransaction 的事务并不会生效
    }

    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public void methodInTransaction() {
        // 对数据库的一些操作
    }
}

为何不会生效?是因为这样的调用不会经过 Spring 的代理,无法通过 Spring 的 advisor 来拦截数据库操作请求。

Spring BeanPostProcessor

首先我们来了解一下 Spring Bean 的生命周期

  1. 调用InstantiationAwareBeanPostProcessor的postProcessBeforeInstantiation(Class<?> beanClass, String beanName)
  2. bean实例化
  3. 调用InstantiationAwareBeanPostProcessor的postProcessAfterInstantiation(Object bean, String beanName)
  4. 调用InstantiationAwareBeanPostProcessor的postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName)
  5. bean注入properties
  6. 分别调用BeanNameAware,BeanClassLoaderAware,BeanFactoryAware中的方法
  7. 调用BeanPostProcessor的postProcessBeforeInitialization(Object bean, String beanName)
  8. 调用InitializingBean的afterPropertiesSet方法
  9. 调用自定义初始化方法
  10. 调用BeanPostProcessor的postProcessAfterInitialization(Object bean, String beanName)
  11. 调用DisposableBean的destroy()方法
  12. 调用自定义销毁方法

作者:土豆肉丝盖浇饭

链接: https://www.jianshu.com/p/6d5c58168493

来源:简书

简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

通过上面的流程我们可以看到,BeanPostProcessor 中的两个方法 postProcessBeforeInitializationpostProcessAfterInitialization 分别在 bean 调用 init 方法前后调用。其中对于对象的代理就是在 postProcessAfterInitialization 方法中完成的,用代理的 bean 来替换原来的 bean

默认情况下, BeanPostProcessor 的职能是通过默认实现类 DefaultAdvisorAutoProxyCreator 实现的,类 DefaultAdvisorAutoProxyCreator 继承自 AbstractAdvisorAutoProxyCreator 该类的继承关系如下图

Spring 中 Transactional 注解原理

DefaultAdvisorAutoProxyCreator 如何代理被 @Transactional 注解的方法所属类

来看看 AbstractAutoProxyCreator 中发生了什么

/**
* Create a proxy with the configured interceptors if the bean is
* identified as one to proxy by the subclass.
* @see #getAdvicesAndAdvisorsForBean
*/
// 类 AbstractAutoProxyCreator
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
    if (bean != null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (!this.earlyProxyReferences.contains(cacheKey)) {
            return wrapIfNecessary(bean, beanName, cacheKey); // 主要逻辑在这里
        }
    }
    return bean;
}

跟进 wrapIfNecessary 方法

/**
* Wrap the given bean if necessary, i.e. if it is eligible for being proxied.
* @param bean the raw bean instance
* @param beanName the name of the bean
* @param cacheKey the cache key for metadata access
* @return a proxy wrapping the bean, or the raw bean instance as-is
*/
// 类 AbstractAutoProxyCreator
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.
    // 查找 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;
}
// AbstractAdvisorAutoProxyCreator 类
@Override
@Nullable
protected Object[] getAdvicesAndAdvisorsForBean(
    Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {

    List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
    if (advisors.isEmpty()) {
        return DO_NOT_PROXY;
    }
    return advisors.toArray();
}
/**
* Find all eligible Advisors for auto-proxying this class.
* @param beanClass the clazz to find advisors for
* @param beanName the name of the currently proxied bean
* @return the empty List, not {@code null},
* if there are no pointcuts or interceptors
* @see #findCandidateAdvisors
* @see #sortAdvisors
* @see #extendAdvisors
*/

// AbstractAdvisorAutoProxyCreator 类
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    // 获取 候选 advisor 
    // BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Advisor.class, true, false);
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    // 筛选 可用的 advisor
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    
    // 此处会获取名称为org.springframework.transaction.interceptor.TransactionInterceptor#0的拦截器
    extendAdvisors(eligibleAdvisors);
    if (!eligibleAdvisors.isEmpty()) {
        eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    return eligibleAdvisors;
}

我们来看看生成的动态类是什么样子的?

Spring 中 Transactional 注解原理

其中有一个 advisor 为

adviceBeanName:org.springframework.transaction.interceptor.TransactionInterceptor#0

TransactionInterceptor 类

我们此时来看看 org.springframework.transaction.interceptor.TransactionInterceptor 里究竟是如何执行 SQL 语句的。我们需要关注的方法为 invoke 方法

@Override
public Object invoke(final MethodInvocation invocation) throws Throwable {
    // Work out the target class: may be {@code null}.
    // The TransactionAttributeSource should be passed the target class
    // as well as the method, which may be from an interface.
    Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);

    // Adapt to TransactionAspectSupport's invokeWithinTransaction...
    return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
        @Override
        public Object proceedWithInvocation() throws Throwable {
            return invocation.proceed();
        }
    });
}

查看 invokeWithinTransaction 方法

// TransactionAspectSupport.java
protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
		throws Throwable {

	// If the transaction attribute is null, the method is non-transactional.
	final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
	final PlatformTransactionManager tm = determineTransactionManager(txAttr);
	final String joinpointIdentification = methodIdentification(method, targetClass);

    // txAttr(事务属性)为空或者tm(事务管理器)为空时,是声明式事务
	if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
		// Standard transaction demarcation with getTransaction and commit/rollback calls.
        // 获取该方法上事务的信息
		TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
		Object retVal = null;
		try {
			// This is an around advice: Invoke the next interceptor in the chain.
			// This will normally result in a target object being invoked.
			retVal = invocation.proceedWithInvocation();
		}
		catch (Throwable ex) {
			// target invocation exception
            // 事务回滚
			completeTransactionAfterThrowing(txInfo, ex);
			throw ex;
		}
		finally {
			cleanupTransactionInfo(txInfo);
		}
		commitTransactionAfterReturning(txInfo);
		return retVal;
	}

	else {
		// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
		try {
			Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr,
					new TransactionCallback<Object>() {
						@Override
						public Object doInTransaction(TransactionStatus status) {
							TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
							try {
								return invocation.proceedWithInvocation();
							}
							catch (Throwable ex) {
								if (txAttr.rollbackOn(ex)) {
									// A RuntimeException: will lead to a rollback.
									if (ex instanceof RuntimeException) {
										throw (RuntimeException) ex;
									}
									else {
										throw new ThrowableHolderException(ex);
									}
								}
								else {
									// A normal return value: will lead to a commit.
									return new ThrowableHolder(ex);
								}
							}
							finally {
								cleanupTransactionInfo(txInfo);
							}
						}
					});

			// Check result: It might indicate a Throwable to rethrow.
			if (result instanceof ThrowableHolder) {
				throw ((ThrowableHolder) result).getThrowable();
			}
			else {
				return result;
			}
		}
		catch (ThrowableHolderException ex) {
			throw ex.getCause();
		}
	}
}

至此,我们基本了解了 Spring 声明式事务的工作流程


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

NoSQL精粹

NoSQL精粹

[美]Pramod J. Sadalage、[美]Martin Fowler / 爱飞翔 / 机械工业出版社 / 2013-8 / 49.00元

《NoSQL精粹》为考虑是否可以使用和如何使用NoSQL数据库的企业提供了可靠的决策依据。它由世界级软件开发大师和软件开发“教父”Martin Fowler与Jolt生产效率大奖图书作者Pramod J. Sadalage共同撰写。书中全方位比较了关系型数据库与NoSQL数据库的异同;分别以Riak、MongoDB、Cassandra和Neo4J为代表,详细讲解了键值数据库、文档数据库、列族数据库......一起来看看 《NoSQL精粹》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

SHA 加密
SHA 加密

SHA 加密工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器