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

原创
2019/04/06 17:42
阅读数 268

问题来自: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

 

以上。

展开阅读全文
打赏
0
1 收藏
分享
加载中
更多评论
打赏
0 评论
1 收藏
0
分享
OSCHINA
登录后可查看更多优质内容
返回顶部
顶部