重點知識複習:Spring+事務

spring IOC&AOP

1. Spring 的四個作用域

singleton 單例—jvm中只會創建一個

Prototype 多例——每次調用如注入或者使用getBean()方法都會創建

Request ——作用於web應用的會話範圍,表示該針對每一次HTTP請求都會產生一個新的bean,同時該bean僅在當前HTTP request內有效

Session——作用於web應用的會話範圍,session和對象進行綁定,session什麼時候失效對象就什麼時候失效

global-session:作用於集羣環境的會話範圍(全局會話範圍),當不是集羣環境時,它就是session

1.1 bean對象的生命週期

單例對象
    出生:當容器創建時對象出生
    活着:只要容器還在,對象一直活着
    死亡:容器銷燬,對象消亡
    總結:單例對象的生命週期和容器相同
多例對象
    出生:當我們使用對象時spring框架爲我們創建
    活着:對象只要是在使用過程中就一直活着。
    死亡:當對象長時間不用,且沒有別的對象引用時,由Java的垃圾回收器回收

2. Spring容器(IOC)創建對象的幾種方式:

調用無參數構造器
帶參數構造器

<!-- 無參構造函數 -->
<bean id="user1" class="com.test.entity.UserEntity" scope="prototype" />
<!-- 有參構造函數 -->
<bean id="user2" class="com.test.entity.UserEntity">
	<constructor-arg name="name" type="java.lang.String"
		value="MingRun"></constructor-arg>
</bean>

工廠創建對象:又分成使用靜態方法和非靜態方法:

<bean id="factory" class="com.test.entity.ObjectFactory"></bean>
<!-- 通過實例工廠方法創建 -->
<bean id="user3" factory-bean="factory" 
	factory-method="getInstance">
</bean>
<!-- 通過靜態工廠方法創建 -->
<bean id="user4" class="com.test.entity.ObjectFactory" 
	factory-method="getStaticInstance"></bean>

3. 倚賴注入(DI),給屬性賦值的幾種方法

使用set方法:

<bean id="userDao" class="cn.test.UserDao"></bean>
<!-- service instance -->
<bean id="userService" class="cn.test.UserService">
	<property name="userDao" ref="userDao"></property>
</bean>

p 名稱空間注入屬性值 (優化)

<!-- 傳統的注入: 
	 <bean id="user" class="cn.test.User" >
	 	<property name="name" value="xxx"></property>
	 </bean>
	-->
	<!-- p名稱空間優化後 -->
<bean id="user" class="cn.test.User" p:name="Jack0001"></bean>

使用註解,步驟如下:
1. 先引入context名稱空間
xmlns:context=“http://www.springframework.org/schema/context”
2. 開啓註解掃描

<context:component-scan base-package="com.test02"></context:component-scan>

3.通過註解的方式,把對象加入ioc容器。

創建對象以及處理對象依賴關係,相關的註解:

@Component   指定把一個對象加入IOC容器
@Repository   作用同@Component; 在持久層使用
@Service      作用同@Component; 在業務邏輯層使用
@Controller    作用同@Component; 在控制層使用 
@Resource     屬性注入

@autowired 和 @resource:@autowired默認使用類型注入,如果找不到bean則使用@resource(name=””),進行名稱注入,其中需要注意的是,@resource註解是jdk1.6及以上帶的,並非是框架所帶的註解。@autowired(required=false)@Qualifier("user")//使用名稱

4. 切面與切入點

切面其實就是抽取出來的那些重複代碼(即關注點)所組成的類。切入點就是需要被執行的方法,如需要添加事務的*del,*add,*update等諸如此類的方法。

5. 註解方式實現AOP編程

<aop:aspectj-autoproxy></aop:aspectj-autoproxy> 開啓事物註解權限

@Aspect		指定一個類爲切面類		
@Pointcut("execution(* com.test.service.UserService.add(..))")  指定切入點表達式

@Before("pointCut_()")	前置通知: 目標方法之前執行
@After("pointCut_()")	後置通知:目標方法之後執行(始終執行)
@AfterReturning("pointCut_()")	返回後通知: 執行方法結束前執行(異常不執行)
@AfterThrowing("pointCut_()")	異常通知:  出現異常時候執行
@Around("pointCut_()")	環繞通知: 環繞目標方法執行
@Component
@Aspect
public class Aop {
	@Before("execution(* com.test.service.UserService.add(..))")
	public void begin() {
		System.out.println("前置通知");
	}

	@After("execution(* com.test.service.UserService.add(..))")
	public void commit() {
		System.out.println("後置通知");
	}

	@AfterReturning("execution(* com.test.service.UserService.add(..))")
	public void afterReturning() {
		System.out.println("運行通知");
	}

	@AfterThrowing("execution(* com.test.service.UserService.add(..))")
	public void afterThrowing() {
		System.out.println("異常通知");
	}

	//環繞通知:在方法的之前和之後執行
	@Around("execution(* com.test.service.UserService.add(..))")
	public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
       System.out.println("我是環繞通知-前");
       //表示放心方法,如果注掉,方法將不會執行
       proceedingJoinPoint.proceed();
       System.out.println("我是環繞通知-後");
	}
}

6. XML方式實現AOP編程

1) 引入jar文件  【aop 相關jar, 4個】
2) 引入aop名稱空間
3)aop 配置
	* 配置切面類 (重複執行代碼形成的類)
	* aop配置
		攔截哪些方法 / 攔截到方法後應用通知代碼
<!-- dao 實例 -->
<bean id="userDao" class="com.test.UserDao"></bean>
<bean id="orderDao" class="com.test.OrderDao"></bean>

<!-- 切面類 -->
<bean id="aop" class="com.test.Aop"></bean>

<!-- Aop配置 -->
<aop:config>
	<!-- 定義一個切入點表達式: 攔截哪些方法 -->
	<aop:pointcut expression="execution(* com.test.*.*(..))" id="pt"/>
	<!-- 切面 -->
	<aop:aspect ref="aop">
		<!-- 環繞通知 -->
		<aop:around method="around" pointcut-ref="pt"/>
		<!-- 前置通知: 在目標方法調用前執行 -->
		<aop:before method="begin" pointcut-ref="pt"/>
		<!-- 後置通知: -->
		<aop:after method="after" pointcut-ref="pt"/>
		<!-- 返回後通知 -->
		<aop:after-returning method="afterReturning" pointcut-ref="pt"/>
		<!-- 異常通知 -->
		<aop:after-throwing method="afterThrowing" pointcut-ref="pt"/>
	</aop:aspect>
</aop:config>

事務

1. 四個特性:

  1. 原子性(Atomicity):原子性是指事務包含的所有操作要麼全部成功,要麼全部失敗回滾
  2. 一致性(Consistency):一致性是指事務必須使數據庫從一個一致性狀態變換到另一個一致性狀態 ,拿轉賬來說,假設用戶A和用戶B兩者的錢加起來一共是5000,那麼不管A和B之間如何轉賬,轉幾次賬,事務結束後兩個用戶的錢相加起來應該還得是5000,這就是事務的一致性。
  3. 隔離性(Isolation):隔離性是當多個用戶併發訪問數據庫時,比如操作同一張表時,數據庫爲每一個用戶開啓的事務,不能被其他事務的操作所幹擾,多個併發事務之間要相互隔離。
  4. 持久性(Durability):持久性是指一個事務一旦被提交了,那麼對數據庫中的數據的改變就是永久性的,即便是在數據庫系統遇到故障的情況下也不會丟失提交事務的操作。

2. Spring 事務分類

手動事務(又稱編程式事務)----自己begin,自己commit。聲明事務----使用xml進行配置(用到了aop技術)。註解方式,添加@transactional

手動式事務:

//Spring聲明式事務管理器類:
//Jdbc技術:DataSourceTransactionManager
//Hibernate技術:HibernateTransactionManager
@Component
public class TransactionUtils {
	// 事物管理器
	@Autowired
	private DataSourceTransactionManager dataSourceTransactionManager;
	public TransactionStatus begin() {
		TransactionStatus transaction = dataSourceTransactionManager.getTransaction(new DefaultTransactionDefinition());
		return transaction;
	}
	public void commit(TransactionStatus transaction) {
		dataSourceTransactionManager.commit(transaction);
	}
	public void rollback(TransactionStatus transaction) {
		dataSourceTransactionManager.rollback(transaction);
	}
}

xml:

	<!-- 開啓註解 -->
	<context:component-scan base-package="com.test"></context:component-scan>
	<!-- 1. 數據源對象: C3P0連接池 -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
		<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"></property>
		<property name="user" value="root"></property>
		<property name="password" value="root"></property>
	</bean>

	<!-- 2. JdbcTemplate工具類實例 -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource"></property>
	</bean>

   <!-- 配置事物 -->
   <bean  id="DataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
   		 <property name="dataSource" ref="dataSource"></property>
   </bean>

在方法中使用:

@Autowired
private UserDao userDao;
@Autowired
TransactionUtils transactionUtils;

public void add() {
	TransactionStatus begin = TransactionUtils.begin();
	userDao.add("gfd", 18);
	int i=1/0;//可能會發生異常
	userDao.add("MingRun", 19);
	TransactionUtils,commit(begin);
}

3. 使用聲明式事務

bean.xml (Spring務管理配置)

	<!-- 開啓註解 -->
	<context:component-scan base-package="com.test"></context:component-scan>
	<!-- 1. 數據源對象: C3P0連接池 -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
		<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"></property>
		<property name="user" value="root"></property>
		<property name="password" value="root"></property>
	</bean>
	<!-- 2. JdbcTemplate工具類實例 -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	<!-- 配置事物 -->
	<bean id="dataSourceTransactionManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
   	<!—配置事物增強-->
	<tx:advice id="txAdvice" transaction-manager="dataSourceTransactionManager">
		<tx:attributes>
			<tx:method name="get*" read-only="true" />
			<tx:method name="find*" read-only="true" />
			<tx:method name="*" read-only="false" />
		</tx:attributes>
	</tx:advice>
	<!-- Aop配置: 攔截哪些方法(切入點表表達式) + 應用上面的事務增強配置 -->
	<aop:config>
		<aop:pointcut expression="execution(* com.test.service.*.*(..))"
			id="pt" />
		<aop:advisor advice-ref="txAdvice" pointcut-ref="pt" />
	</aop:config>

4. 註解方式實現

@Transactional註解:
1. 應用事務的註解
2. 定義到方法上: 當前方法應用spring的聲明式事務
3. 定義到類上: 當前類的所有的方法都應用Spring聲明式事務管理;
4. 定義到父類上: 當執行父類的方法時候應用事務。

Bean.xml

<!-- 開啓註解 -->
<context:component-scan base-package="com.test"></context:component-scan>
<!-- 1. 數據源對象: C3P0連接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
	<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
	<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"></property>
	<property name="user" value="root"></property>
	<property name="password" value="root"></property>
</bean>

<!-- 2. JdbcTemplate工具類實例 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
	<property name="dataSource" ref="dataSource"></property>
</bean>

<!-- 配置事物 -->
<bean id="dataSourceTransactionManager"
	class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 開啓註解事物 -->
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>

UserService

@Transactional
public void add() {
	try {
		userDao.add("gfd", 18);
		int i = 1 / 0;
		userDao.add("MingRun", 19);
	} catch (Exception e) {
		TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
	}		
}

5. 事物實現原理

事物底層實現通過AOP環繞通知實現
代碼:

public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
	TransactionStatus begin = transactionUtils.begin();
	System.out.println("我是環繞通知-開啓事物");
	try {
		proceedingJoinPoint.proceed();
		transactionUtils.commit(begin);
		System.out.println("我是環繞通知-提交事物");
	} catch (Exception e) {
		System.out.println("我是環繞通知-回滾事物");
		transactionUtils.rollback(begin);
	}
}

6. 使用事物注意事項

使用事物時,一定要將異常拋出外部,不然AOP環繞通知獲取不到異常不能夠回滾。
事務開啓之後一定要釋放,通過提交或者回滾,否則會一直佔用內存資源。

7. 傳播行爲

Propagation(key屬性確定代理應該給哪個方法增加事務行爲。這樣的屬性最重要的部份是傳播行爲。)有以下選項可供使用:

  • PROPAGATION_REQUIRED:支持當前事務,如果當前沒有事務,就新建一個事務。這是最常見的選擇。
  • PROPAGATION_REQUIRES_NEW:新建事務,如果當前存在事務,把當前事務掛起。
  • PROPAGATION_SUPPORTS :支持當前事務,如果當前沒有事務,就以非事務方式執行。(如果當前有事物,我就用當前事物,如果當前沒有事物,就以非事物進行執行)
  • PROPAGATION_NOT_SUPPORTED :以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。
  • PROPAGATION_NEVER :以非事務方式執行,如果當前存在事務,則拋出異常。
  • PROPAGATION_MANDATORY:支持當前事務,如果當前沒有事務,就拋出異常。
  • PROPAGATION_NESTED:如果當前存在事務,則在嵌套事務內執行。如果當前沒有事務,則執行與PROPAGATION_REQUIRED類似的操作。
@Transactional(
	readOnly = false,  // 讀寫事務
	timeout = -1,       // 事務的超時時間不限制
	noRollbackFor = ArithmeticException.class,  // 遇到數學異常不回滾
	isolation = Isolation.DEFAULT,              // 事務的隔離級別,數據庫的默認
	propagation = Propagation.REQUIRED			// 可以看到事務默認的傳播行爲REQUIRED
)

例一:

class UserService{
	@Autowired
	LogService logService;
	
	@Transactional(propagation=Propagation.REQUIRED)
	public void  saveUser(){
		logService.insertLog();    // 加入當前事務
		1/0; //.. 異常, 會回滾
		//save User 操作
	}
}
class LogService{
	public void insertLog(){
		//執行插入日誌操作
	};  
}

REQUIRED默認事務,方法insertLog()並沒有添加事務,但會使用saveUser()的事務,兩個方法都會回滾。

例二:

class UserService{
	@Autowired
	LogService logService;
	
	@Transactional(propagation=Propagation.REQUIRED)
	public void  saveUser(){
		logService.insertLog();    // 加入當前事務
		1/0; //.. 異常, 會回滾
		//save User 操作
	}
}
class LogService{
	@Transactional(propagation=Propagation.REQUIRED)
	public void insertLog(){
		//執行插入日誌操作 
	};  
}

結果saveUser()方法回滾,insertLog()不回滾,REQUIRED只在沒有事務的時候添加事務。

例三:

class UserService{
	@Autowired
	LogService logService;
	
	@Transactional(propagation=Propagation.REQUIRED)
	public void  saveUser(){
		logService.insertLog();    // 加入當前事務
		1/0; //.. 異常, 會回滾
		//save User 操作
	}
}
class LogService{
	@Transactional(propagation=Propagation.REQUIRED_REQUIRES_NEW)
	public void insertLog(){
		//執行插入日誌操作 
	};  
}

前者回滾,而insertLog不回滾,REQUIRED_REQUIRES_NEW表示不用當前的事務,也就是不使用saveUser的事務,只用自己的事務。

通過例子剩下的幾種就更好理解了
PROPAGATION_SUPPORTS:有事務就還用你的事務,沒有就沒了。
PROPAGATION_NOT_SUPPORTED:當前方法就不加事務,就算外層添加了事務也不用,直接掛起。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章