Spring 事务

来源:互联网 发布:淘宝怎么心极多点 编辑:程序博客网 时间:2024/06/15 01:12

基于JDK接口代理

jdk代理后生成一个实现了接口的实现类(区别于自己写的实现类)

public final classWorldServiceProxy extendsProxy implements WorldService, SpringProxy,Advised

需要注意的是@Transactional标注的有效位置,只有在有效位置标注了@Transactional,在JdkDynamicAopProxy.invoke()中才能获取到Transaction Advisor来处理事务。否则,要么不会生成代理,要么即使生成了代理,代理也不会真正去管理事务,这两种情况都不会开启事务。有效标注位置为:1)接口上:会为实现类(目标类)里的所有实现自接口的方法开启事务,包括final的。2)目标类上:会为实现类(目标类)里的所有实现自接口的方法开启事务,包括final的。。3)接口方法上:会为实现类(目标类)里实现自接口的方法且在接口里标注了@Transactional的方法开启事务,包括final的。4)目标类方法上:会为标注了@Transactional的且实现自接口的方法开启事务,包括final的。5)以上4种情况并集。特别注意,以上几种情况都只能通过调用代理类才会开启事务,代理类实例是通过容器注入或从容器中获取的。下面几种场景,事务并不起作用:1)直接调用目标类的实例,如,WorldServicews = new WorldServiceImpl();ws.world1();通过上面原理阐述,不难知道,虽然world1()标注了@Transactional,但ws并未通过代理调用world1(),也就不能通过Transaction Advisor处理事务。2)目标类方法自调用,根据【代码1】,如下方式调用,ApplicationContextctx = new ClassPathXmlApplicationContext("applicationContext.xml");WorldServicews = (WorldService)ctx.getBean("worldService");ws.world2();虽然ws指向的是代理类实例,world1()也加了@Transacional注解,但因为world2()并未加@Transacional注解,world2()也就不具有事务。通过world2()调用world1(),是通过this调用,也就是直接调用的目标类实例,所以也就不能通过代理为world1()开启事务。

Cglib代理

cglib代理后生成一个继承我们自己写的目标类的子类

PublicclassHelloServiceProxyextendsHelloService

有效标注位置为:1)目标类上:会为目标类里的所有public非final的实例方法开启事务。2)目标类方法上:会为标注了@Transactional的且为public非final的实例方法开启事务。3)以上2种情况并集。其他标注位置均无效。看到这里,大家可能会产生疑问,为什么protected和package可见性的非final实例方法上标注了@Transactional注解,不能开启事务呢?这些方法也是可以在子类中重写的呀?的确,在java继承体系中,子类能够重写父类中的public或protected或相同包下package可见性的,且非final的实例方法。理论上,也是可以为protected和protected方法如同public一样通过子类中重写的方法开启事务的,实际却不能,这或许是Spring作者基于某些原因对实现做出的取舍吧。特别注意,以上几种情况,类似JDK接口代理的原理,下面几种场景,事务并不起作用:1)直接调用目标类的实例2)目标类方法自调用


常见陷阱
@Transactional标注无效的方法

1) JDK接口代理:
除了实现自接口的方法(包括final的),其他方法均无效。

2) Cglib代理:
除了public的非final的实例方法,其他方法均无效。无效的有static的,private,protected,package可见范围的,public final的。

类方法自调用
如前文所述,在目标类内部通过this调用一个标注了@Transactional的方法,事务不会起作用。

事务不起作用其根本原因就是未通过代理调用,因为事务是在代理中处理的,没通过代理,也就不会有事务的处理。


0 0