菜瓜:上次的AOP理论知识看完收获挺多的,虽然有一个自定义注解的demo,但还是觉得差点东西
水稻:我也觉得没有跟一遍源码还是差点意思,这次结合@Transactional注解深入源码看一下
菜瓜:事务注解,这个平时用的挺多的
水稻:是吗?来看看你的基础咋样
菜瓜:虽然没看过源码,我大胆猜测一下
/** * @author QuCheng on 2020/6/24. */ @Service public class ItemServiceImpl implements ItemService { @Resource private IcbcItemMapper itemMapper; @Resource private ItemService itemService; @Override public void changeNameById(Long itemId) { // changeItemById(itemId); itemService.changeItemById(itemId); } @Transactional(rollbackFor = RuntimeException.class) @Override public void changeItemById(Long itemId) { itemMapper.updateNameById(itemId, "name4"); int a = 10 / 0; itemMapper.updatePriceById(itemId, 100L); } }
水稻:可以啊,平时的代码没白写
菜瓜:coding这种事情,easy啦!
水稻:这就飘了?来看这个问题
菜瓜:。。。这不就是大事务嵌套小事务嘛。。。我不会
水稻:不扯了,来看源码吧,这个问题等解释了传播属性你就知道了
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> { @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; } } 。。。 } @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()); 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() { // 增强 TransactionInterceptor interceptor = new TransactionInterceptor(); interceptor.setTransactionAttributeSource(transactionAttributeSource()); if (this.txManager != null) { interceptor.setTransactionManager(this.txManager); } return interceptor; } }
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable { 。。。 @Override @Nullable public Object invoke(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, invocation::proceed); } } public abstract class TransactionAspectSupport implements BeanFactoryAware, InitializingBean { 。。。 @Nullable protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass, final InvocationCallback invocation) throws Throwable { 。。。 // ①创建事务,数据库连接处理也在这 TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification); Object retVal = null; try { // 调用目标方法 retVal = invocation.proceedWithInvocation(); } catch (Throwable ex) { // 异常后事务处理 completeTransactionAfterThrowing(txInfo, ex); throw ex; } finally { cleanupTransactionInfo(txInfo); } commitTransactionAfterReturning(txInfo); return retVal; } 。。。 }
菜瓜:懂,接下来的代码逻辑就是在增强类TransactionInterceptor的invoke方法里
水稻:对
protected void doBegin(Object transaction, TransactionDefinition definition) { 。。。 // 如果连接是新的,就进行绑定 if (txObject.isNewConnectionHolder()) { TransactionSynchronizationManager.bindResource(this.obtainDataSource(), txObject.getConnectionHolder()); } 。。。 } class TransactionSynchronizationManager public static void bindResource(Object key, Object value) throws IllegalStateException { Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key); Assert.notNull(value, "Value must not be null"); Map<Object, Object> map = resources.get(); // set ThreadLocal Map if none found if (map == null) { map = new HashMap<>(); resources.set(map); } Object oldValue = map.put(actualKey, value); // Transparently suppress a ResourceHolder that was marked as void... if (oldValue instanceof ResourceHolder && ((ResourceHolder) oldValue).isVoid()) { oldValue = null; } if (oldValue != null) { throw new IllegalStateException("Already value [" + oldValue + "] for key [" + actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]"); } if (logger.isTraceEnabled()) { logger.trace("Bound value [" + value + "] for key [" + actualKey + "] to thread [" + Thread.currentThread().getName() + "]"); } }
@Transactional(rollbackFor = RuntimeException.class) @Override public void changeNameById(Long itemId) { itemMapper.updateNameById(itemId, "A"); try { itemService.changeItemById(itemId); // itemService.changeItemByIdNested(itemId); } catch (Exception e) { System.out.println("我想继续执行,不影响修改A"); } itemMapper.updatePriceById(itemId, 1L); } @Transactional(rollbackFor = RuntimeException.class) @Override public void changeItemById(Long itemId) { itemMapper.updateNameById(itemId, "B+REQUIRED"); itemMapper.updatePriceById(itemId, 10L); int a = 10 / 0; } @Transactional(rollbackFor = RuntimeException.class, propagation = Propagation.NESTED) @Override public void changeItemByIdNested(Long itemId) { itemMapper.updateNameById(itemId, "B+NESTED"); itemMapper.updatePriceById(itemId, 100L); int a = 10 / 0; } -- 测试结果 //① itemService.changeItemById(itemId); 数据库所有数据都不会改变 我想继续执行,不影响修改A org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only // ② itemService.changeItemByIdNested(itemId); 第一个方法的修改会生效 我想继续执行,不影响修改A
菜瓜:这就是传播属性NESTED?默认的是REQUIRED,还有一个常用的REQUIRES_NEW呢?
水稻:搞清楚这个其实从数据库连接入手其实就很清楚
菜瓜:传播属性了解。回滚的问题还得再看看,篇幅很大是很复杂吗?
水稻:其实不复杂,就是要跟踪源码断点调试。。。截图搞来搞去,篇幅就很长,你自己去调的话其实很快
菜瓜:那我下去康康
总结