前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >关于在Spring 中方法内部调用自身方法事务 REQUIRE_NEW 不生效的解释

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

作者头像
Dylan Liu
发布2019-07-01 13:22:39
1.4K0
发布2019-07-01 13:22:39
举报
文章被收录于专栏:dylanliudylanliu

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

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

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

看以下代码

代码语言:javascript
复制
public interface DynamicProxyInterface {
    void a();

    void b();
}
代码语言:javascript
复制
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();
    }
}

执行结果为:

代码语言:javascript
复制
invoke in proxy
this is a
this is b

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

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

代码语言:javascript
复制
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 方法里最后加入测试代码:

代码语言:javascript
复制
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 输出结果,与上面一样)

代码语言:javascript
复制
invoke in proxy
this is a
this is b
invoke in cglib
this is a
this is b

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

代码语言:javascript
复制
AopContext.currentProxy();

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

代码语言:javascript
复制
SpringTransactionMyBatisService  service = (SpringTransactionMyBatisService)AopContext.currentProxy();
service.saveNew();

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

以上。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档