在使用Spring的註解事務時候,咱們發現內部方法聲明的事務不起做用,而是決定於外部方法註解的事務。究竟是真不起做用,仍是咱們Spring的事務註解機制理解錯了,致使誤用了。下面咱們看兩個例子:java
測試類:spring
package com.aop;
數據庫
import org.springframework.beans.factory.BeanFactory;
ide
import org.springframework.context.support.ClassPathXmlApplicationContext;
測試
import com.thread.service.IBaseFacadeService;
this
//@RunWith(SpringJUnit4ClassRunner.class)
spa
//@ContextConfiguration(locations = { "classpath:/spring-ibatis.xml", "classpath:/spring-jdbctemplate.xml" })
.net
public class TestStudentDao {
代理
public static void main(String[] args) {
日誌
try {
BeanFactory factory = new ClassPathXmlApplicationContext("spring-jdbctemplate.xml");
IBaseService service = (IBaseService) factory.getBean("businessSerivce");
service.doA();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
第一例子:內部方法事務不起做用:
package com.aop;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.entity.Student;
@Service("businessSerivce")
public class BusinessServiceImpl implements IBaseService {
@Autowired
IStudentDao studentDao;
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, rollbackFor = Exception.class)
public String doA() throws Exception {
Student st = new Student();
st.setId(1);
st.setSex("girl");
st.setUsername("zx");
studentDao.insertStudent(st);
System.out.println(this);
System.out.println("是不是代理調用,AopUtils.isAopProxy(this) : " + AopUtils.isAopProxy(this));
System.out.println("是不是cglib類代理調用,AopUtils.isCglibProxy(this) : " + AopUtils.isCglibProxy(this));
System.out.println("是不是jdk動態接口代理調用,AopUtils.isJdkDynamicProxy(this) : " + AopUtils.isJdkDynamicProxy(this));
this.doB();
int i = 1 / 0;// 拋出異常,doB()的事務事務回滾
return "success";
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.DEFAULT, rollbackFor = Exception.class)
public String doB() throws Exception {
Student st = new Student();
st.setId(2);
st.setSex("girl");
st.setUsername("zx2");
studentDao.insertStudent(st);
return "success";
}
}
測試類執行結果:
com.aop.BusinessServiceImpl@5a47cf7
是不是代理調用,AopUtils.isAopProxy(this) : false
是不是cglib類代理調用,AopUtils.isCglibProxy(this) : false
是不是jdk動態接口代理調用,AopUtils.isJdkDynamicProxy(this) : false
java.lang.ArithmeticException: / by zero
從測試類執行結果能夠看到,咱們沒有成功插入數據。理論來講方法doB()使用Propagation.REQUIRES_NEW傳播行爲,咱們應該在數據庫中插入姓名爲zx2的數據,可是倒是事與願違,程序沒有按照咱們想要的方向走,不急,咱們再看下面的例子:
第二例子:內部方法事務起做用
package com.aop;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.entity.Student;
@Service("businessSerivce")
public class BusinessServiceImpl implements IBaseService,ApplicationContextAware {
@Autowired
IStudentDao studentDao;
@Autowired
ApplicationContext context;
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, rollbackFor = Exception.class)
public String doA() throws Exception {
Student st = new Student();
st.setId(1);
st.setSex("girl");
st.setUsername("zx");
studentDao.insertStudent(st);
//BusinessServiceImpl service1 = (BusinessServiceImpl)context.getBean(IBaseService.class);
BusinessServiceImpl service = (BusinessServiceImpl)context.getBean("businessSerivce");
//System.out.println(service1);
System.out.println(service);
System.out.println("是不是代理調用,AopUtils.isAopProxy(service) : " + AopUtils.isAopProxy(service));
System.out
.println("是不是cglib類代理調用,AopUtils.isCglibProxy(service) : " + AopUtils.isCglibProxy(service));
System.out.println("是不是jdk動態接口代理調用,AopUtils.isJdkDynamicProxy(service) : "
+ AopUtils.isJdkDynamicProxy(service));
// 使用代理調用方法doB()
service.doB();
int i = 1 / 0;// 拋出異常,doB()的事務事務回滾
return "success";
}
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.DEFAULT, rollbackFor = Exception.class)
public String doB() throws Exception {
Student st = new Student();
st.setId(2);
st.setSex("girl");
st.setUsername("zx2");
studentDao.insertStudent(st);
return "success";
}
@Override
public void setApplicationContext(ApplicationContext arg0) throws BeansException {
context = arg0;
}
}
測試類執行結果:
com.aop.BusinessServiceImpl@7b619bbf
是不是代理調用,AopUtils.isAopProxy(service) : true
是不是cglib類代理調用,AopUtils.isCglibProxy(service) : true
是不是jdk動態接口代理調用,AopUtils.isJdkDynamicProxy(service) : false
java.lang.ArithmeticException: / by zero
從執行結果來看,數據庫中插入了一條數據,而這也正是咱們想要的結果,數據庫中插入的是doB()方法執行的結果,doA()執行結果被回滾了。
例一和例二有什麼區別嗎?咱們來看打印出來的日誌,例一中打印出調用doB()方法的對象this不是代理對象,而是代理目標對象。而例二調用doB()方法的對象service是代理對象,不是代理目標對象。最後例一沒有咱們達到咱們的目的,而例二達到了咱們目的。爲何代理目標對象執行doB()方法,聲明在本身上面的事務就失效了,而代理對象執行doB()方法就成功了呢?這是由於spring在掃描全部類中@Transaction標記的方法的全部類,是經過代理對象植入事務這個特殊的前置加強(advise)的,而不是在代理目標對象上植入加強(advise)的。因此最後就出現了咱們上面的兩種結果。
注意咱們這裏使用的是Cglib動態代理(類代理)
因此咱們在類BusinessServiceImpl中強制轉換使用類強制轉換
BusinessServiceImpl service = (BusinessServiceImpl)context.getBean("businessSerivce");
可是若是咱們使用jdk動態代理<aop:aspectj-autoproxy proxy-target-class=false/>
那咱們就得使用接口強制轉換:
IBaseService service = (IBaseService)context.getBean("businessSerivce");