1. 程式人生 > >Spring傳播行為內部方法不起作用

Spring傳播行為內部方法不起作用

在使用Spring的註解事務時候,我們發現內部方法宣告的事務不起作用,而是決定於外部方法註解的事務。到底是真不起作用,還是我們Spring的事務註解機制理解錯了,導致誤用了。下面我們看兩個例子:

測試類:

package com.aop;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.thread.service.IBaseFacadeService;


//@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration(locations = { "classpath:/spring-ibatis.xml", "classpath:/spring-jdbctemplate.xml" })
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;

	@Override
	@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";
	}

}
測試類執行結果:

[email protected]
是否是代理呼叫,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;
		
	}

}
測試類執行結果:

[email protected]
是否是代理呼叫,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");