spring声明式事务的一些坑

总结

  1. 只支持public方法
  2. 无法支持内部调用

场景和原理分析

场景

  • 只支持public方法
    @Transactional
    public List getAreaList(int pageNo, int pageSize) {
        RowBounds rowBounds = new RowBounds(pageNo, pageSize);
        return areaMapper.getAreaList(rowBounds);
    }
  • 无法支持内部调用
@Override
    public List getAreaList(int pageNo, int pageSize) {
        RowBounds rowBounds = new RowBounds(pageNo, pageSize);
        return areaMapper.getAreaList(rowBounds);
        transactionTest();
    }
    
    @Transactional
    public void transactionTest(){
        //假装有需要事务的操作
    }

当然,idea还是很智能的


idea提示

原理分析

我们都知道,spring的注解功能基本上都是通过spring aop,注解加切面才能完美的实现各种功能。当然,spring aop都是通过生成代理对象的方式实现的,也就是在代理对象中对你想要调用的方法进行了增强。
反之而言,你不使用代理对象,注解无效。接下来看@Transactional的实现方式。
在TransactionInterceptor中可以看到

@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中的方法,下面贴关键代码:

    // 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;

明显可以看到只是在方法前后进行了增强,也就是说内部调用方法未使用代理对象,无法进行增强也就无法开启事务。
@Transactional 只能应用到 public 方法才有效
只有@Transactional 注解应用到 public 方法,才能进行事务管理。这是因为在使用 Spring AOP 代理时,Spring 在调用在图 1 中的 TransactionInterceptor 在目标方法执行前后进行拦截之前,DynamicAdvisedInterceptor(CglibAopProxy 的内部类)的的 intercept 方法或 JdkDynamicAopProxy 的 invoke 方法会间接调用 AbstractFallbackTransactionAttributeSource(Spring 通过这个类获取表 1. @Transactional 注解的事务属性配置属性信息)的 computeTransactionAttribute 方法。如图:

protected TransactionAttribute computeTransactionAttribute(Method method,
    Class targetClass) {
        // Don't allow no-public methods as required.
        if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;}

引用:https://www.ibm.com/developerworks/cn/java/j-master-spring-transactional-use/index.html

你可能感兴趣的:(spring声明式事务的一些坑)