关于在Spring 中方法内部调用自身方法事务 REQUIRE_NEW 不生效的解释

问题来自:Spring事务的传播行为中REQUIRES_NEW真的有效吗

 

这个是Spring 对拦截的实现有关。Spring 拦截实现的方法是动态生成一个代理类。正常使用 @Autowired 注解注入的实际上就是这个代理类。

一。 对于有接口实现的类代理,Spring 使用的是 Java 自带的代理生成方式。这种方式对 target.method() 方式的调用是可以拦截到的,对于类内调用 method() 方式则拦截不到。

看以下代码

public interface DynamicProxyInterface {
    void a();

    void b();
}
public class DynamicProxy implements DynamicProxyInterface
{
    @Override
    public void a() {
        System.out.println("this is a");
        b();
    }

    @Override
    public void b() {
        System.out.println("this is b");
    }

    public static void main(String[] args) {
        DynamicProxy target = new DynamicProxy();
        DynamicProxyInterface dynamicProxy = (DynamicProxyInterface) Proxy.newProxyInstance(DynamicProxy.class.getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("invoke in proxy");
                return method.invoke(target, args);
            }
        });

        dynamicProxy.a();
    }
}

执行结果为:

invoke in proxy
this is a
this is b


从这可以看出你类内自行调用方法是不会被代理拦截到的,因此你使用的事务注解也就不会生效。

二。 对于单纯的class,没有接口,则 Spring 使用 cglib 进行代理,这里 Spring实现了自己的 CallbackFilter,具体类可以参见 Spring 源码CglibAopProxy ,在目标类的invoke方法中,我们可以看到这块代码

public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
			Object oldProxy = null;
			Object target = this.targetSource.getTarget();//获取原始对象(未被代理)
			try {
				oldProxy = AopContext.setCurrentProxy(proxy);
				Object retVal = methodProxy.invoke(target, args);
				return processReturnType(proxy, target, method, retVal);
			}
			finally {
				AopContext.setCurrentProxy(oldProxy);
				this.targetSource.releaseTarget(target);
			}
		}

在第二行,我们看到 Spring 获取当前被代理的对象,直接进行invoke,类内方法也不会被cglib 代理到

我们写一个测试方法来试下,在上面main 方法里最后加入测试代码:

Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(DynamicProxy.class);
        enhancer.setCallback(new MethodInterceptor() {
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("invoke in cglib");
                return methodProxy.invoke(target, objects);

            }
        });

        DynamicProxy cglibProxy = (DynamicProxy) enhancer.create();
        cglibProxy.a();
    }

执行结果(上面是Java proxy 输出结果,与上面一样)

invoke in proxy
this is a
this is b
invoke in cglib
this is a
this is b

 

Spring 针对这种情况通过 threadlocal 的方式暴露了当前类的代理,可以使用  

AopContext.currentProxy();

方式得到,使用获取到的代理类再调用方法就可以再次走事务的处理逻辑了。即

SpringTransactionMyBatisService  service = (SpringTransactionMyBatisService)AopContext.currentProxy();
service.saveNew();

 

最后,如果你使用的 xml 配置,那么需要在 aop 配置中,设置 expose-proxy 为true

 

以上。

转载于:https://my.oschina.net/liufq/blog/3033055

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值