AOP 事务缺陷
参考这个文档
http://liuu.iteye.com/blog/422810
?
http://www.51cto.com/specbook/223/39480.htm
?
http://www.iflym.com/index.php/code/proxy-created-of-subclass-extended-proxied-class-by-spring.html
?
http://www.iflym.com/index.php/code/proxy-method-intercept-of-subclass-extended-proxied-class-by-spring.html
?
http://www.iteye.com/problems/71797
?
http://www.iteye.com/topic/40553
?
http://www.redsaga.com/spring_ref/2.5/html/aop-api.html
?
http://japi.iteye.com/blog/285800
?
或许按照回复的办法,把参考文档中的两个方法放到不同类中。
很不幸,我也遇到了这个问题。
但是按照作者的方法,修改aop:config如下:
??? <aop:config proxy-target-expression="execution(* *..service.*Service.*(..)) " />
????? <aop:advisor pointcut-ref="allServiceMethod" advice-ref="txAdvice" />
? </aop:config>
其中service包下的是接口,具体实现在service.spring包下面。
修改后还是不行。
但改成这样就好了:
<aop:config>
<aop:pointcut id="allServiceMethod" expression="execution(* *..service.*Service.*(..)) and execution(* *..service.*.*Service.*(..))" />
<aop:advisor pointcut-ref="allServiceMethod" advice-ref="txAdvice" />
?</aop:config>
?
也就是在接口和实现类都声明在事务中。
?如果只是在声明接口或者实现类单方面加都不行。
修改后虽然能写数据库,但是事务不生效!!!很奇怪的问题!!!!
?
我的情况如下:
action调用service.A? ;serviceA调用service.B;
事务设置采用声明式事务
其中service.A的方法属于只读事务;B方法属于读写事务;然后B又调用service2.B,真正写数据库的是service2.B。
调用时报错,只读事务,不能修改数据。
查看堆栈发现,代理只在调用A时加了事务拦截,在A调用B是没加事务拦截;
堆栈大概如下,注意红色字体是连在一次调用,中间没有任何拦截:
?at xxx.service.spring.ServiceImpl.B(ServiceImpl.java:1245)
?at xxx.service.spring.ServiceImpl.A(ServiceImpl.java:1555)
?at xxx.service.spring.ServiceImpl$$FastClassByCGLIB$$5bad9769.invoke(<generated>)
?at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:149)
?at org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.invokeJoinpoint(Cglib2AopProxy.java:700)
?at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:149)
?at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:106)
?at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
?at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:89)
?at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
?at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:635)
?at xxx.action$$EnhancerByCGLIB$$f76d57d.xxx(<generated>)
?
?
?
如果是required,则事务依旧使用原来的事务,所以A只读,导致B只读,把service2.B改成REQUIRED_NEW就好了但没什么特殊要求都应该使用REQUIRED。
这个问题引起的原因是这个配置
?<tx:method name="*" propagation="REQUIRED" read-only="true"/>
导致A变成只读,范围太大了。
上面改后,service.B中依旧是只读事务。
?
测试发现AOP不能嵌套,不管JDK还是cglib都一样,如果同一对象OBJ的第一个函数A没有使用事务,那么A里面调用的OBJ.B方法,尽管B方法已经声明了事务,但一样不生效,因为AOP没有对A调用B是做拦截,也不可能做,B在OBJ中调用,不是在代理中调用,代理没法做。跨类到可以。
只读原因分析:
所以service.A只读事务,导致service.A调用B时,没经过AOP事务拦截,也就是service.A和service.B在同一个事务中,B就是只读事务,而当B调用service2.B时,如果service2.B是REQUIRED时,尽管service2.B的AOP拦截生效,但是REQUIRED命令service2.B直接使用已有事务,导致service2.B中也是只读事务。
?
把
?<tx:method name="*" propagation="REQUIRED" read-only="true"/>
去掉或者细化就好了
?
?