SpringBoot AOP如何配置全局事務(wù)
SpringBoot的出現(xiàn)使得項(xiàng)目中使用事務(wù)變得非常簡(jiǎn)單,有兩種使用方式,適合小型項(xiàng)目的注解事務(wù)(聲明式事務(wù)管理),適合大型項(xiàng)目的全局事務(wù)。
1、注解事務(wù)(次要)
注解事務(wù)使用只用兩步,開啟事務(wù)注解功能,使用事務(wù)注解功能,并且每步都只有使用一個(gè)注解。
第一步
開啟事務(wù)注解功能@EnableTransactionManagement
在主啟動(dòng)類中添加注解@EnableTransactionManagement即可。
package com.gx;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@EnableTransactionManagement //開啟事務(wù)注解功能
@SpringBootApplication
public class Ch09SpringbootTransAnnoApplication {
public static void main(String[] args) {
SpringApplication.run(Ch09SpringbootTransAnnoApplication.class, args);
}
}
第二步
使用事務(wù)注解功能@Transactional
在service接口實(shí)現(xiàn)類或接口實(shí)現(xiàn)類方法上添加@Transactional即可。
package com.gx.service.impl;
import com.gx.service.StudentService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.gx.domain.Student;
@Service
public class StudentServiceImpl implements StudentService {
@Transactional //使用注解事務(wù)
@Override
public String addStudent(Student student) {
//業(yè)務(wù)方法
}
}
注意事項(xiàng):@Transactional必須添加在public修飾的方法上。
2、全局事務(wù)(主要)
SpringBoot全局事務(wù)主要使用AOP切面編程。
第一步
創(chuàng)建切面類@Aspect。
創(chuàng)建一個(gè)普通的類,加上@Aspect后該類就是一個(gè)切面類了,用于編寫事務(wù)功能。
同時(shí)還需要把該切面類定義為一個(gè)配置類,添加注解@Configuration即可。
注意事項(xiàng):
1、@Aspect將該類定義為切面類,把當(dāng)前類作為一個(gè)切面被容器讀取。
? 2、@Configuration將該類定義為配置類,配置spring容器,注入bean
package com.gx.config;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.context.annotation.Configuration;
@Aspect //定義切面類,把當(dāng)前類標(biāo)識(shí)為一個(gè)切面供容器讀取
@Configuration //定義配置類
public class TransactionAdviceConfig {
//增強(qiáng)方法
}
第二步
創(chuàng)建第一個(gè)方法,返回事務(wù)攔截器(TransactionInterceptor),聲明業(yè)務(wù)方法的事務(wù)屬性,并且注冊(cè)到bean中。
需要返回事務(wù)攔截器TransactionInterceptor,就需要new一個(gè)TransactionInterceptor。
根據(jù)TransactionInterceptor的類可得知,創(chuàng)建一個(gè)TransactionInterceptor目前只有兩個(gè)方法。
public TransactionInterceptor() {
}
public TransactionInterceptor(TransactionManager ptm, TransactionAttributeSource tas) {
this.setTransactionManager(ptm);
this.setTransactionAttributeSource(tas);
}
要使用事務(wù),就要有事務(wù)管理器TransactionManager和事務(wù)屬性TransactionAttributeSource。
配置事務(wù)屬性時(shí)一般都是通過(guò)方法的名字篩選,比如add*、save*、delete*等,所以事務(wù)屬性使用的是他的子類NameMatchTransactionAttributeSource。
//事務(wù)管理器
@Autowired
private TransactionManager transactionManager;
@Bean
public TransactionInterceptor txAdvice() {
//聲明一個(gè)通過(guò)方法名字配置事務(wù)屬性的對(duì)象
NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
//返回一個(gè)事務(wù)攔截器
return new TransactionInterceptor(transactionManager, source);
}
通過(guò)方法名稱設(shè)置業(yè)務(wù)方法事務(wù)屬性。
NameMatchTransactionAttributeSource類中我們使用的頻繁的就兩個(gè)方法。
setNameMap其實(shí)就是addTransactionalMethod的集合。
//通過(guò)map集合給多個(gè)方法或者多類方法設(shè)置事務(wù)屬性
public void setNameMap(Map<String, TransactionAttribute> nameMap) {
nameMap.forEach(this::addTransactionalMethod);
}
//通過(guò)方法名稱或一類方法名稱和事務(wù)屬性,給一個(gè)方法或一類方法設(shè)置事務(wù)屬性
public void addTransactionalMethod(String methodName, TransactionAttribute attr) {
if (logger.isDebugEnabled()) {
logger.debug("Adding transactional method [" + methodName + "] with attribute [" + attr + "]");
}
if (this.embeddedValueResolver != null && attr instanceof DefaultTransactionAttribute) {
((DefaultTransactionAttribute) attr).resolveAttributeStrings(this.embeddedValueResolver);
}
this.nameMap.put(methodName, attr);
}
設(shè)置事務(wù)屬性。
TransactionAttribute:事務(wù)屬性,有很多實(shí)現(xiàn)的實(shí)現(xiàn)類,一般使用基于規(guī)則的事務(wù)屬性RuleBasedTransactionAttribute。
功能大部分在他的父類DefaultTransactionDefinition中。
只寫一小部分,其他可以根據(jù)業(yè)務(wù)需求寫事務(wù)屬性。
//配置一個(gè)事務(wù)屬性(只讀)
RuleBasedTransactionAttribute readOnlyTx = new RuleBasedTransactionAttribute();
//是否只讀
readOnlyTx.setReadOnly(true);
//事務(wù)傳播行為
readOnlyTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
//通過(guò)方法名稱設(shè)置業(yè)務(wù)方法事務(wù)屬性
Map<String, TransactionAttribute> txMap = new HashMap<>();
txMap.put("get*", readOnlyTx);
再添加到NameMatchTransactionAttributeSource中
source.setNameMap(txMap);
設(shè)置事務(wù)屬性的方法
- 1、事務(wù)傳播行為 setPropagationBehavior();
- 2、事務(wù)隔離級(jí)別 setIsolationLevel();
- 3、事務(wù)超時(shí)時(shí)間 setTimeout();
- 4、事務(wù)只讀 setReadOnly();
- 5、設(shè)置事務(wù)名稱 setName();
- 6、設(shè)置回滾規(guī)則 setRollbackRules();
事務(wù)屬性。(擴(kuò)展)
事務(wù)傳播行為
| 事務(wù)行為 | 說(shuō)明 |
|---|---|
| PROPAGATION_REQUIRED | 支持當(dāng)前事務(wù),假設(shè)當(dāng)前沒有事務(wù)。就新建一個(gè)事務(wù) |
| PROPAGATION_SUPPORTS | 支持當(dāng)前事務(wù),假設(shè)當(dāng)前沒有事務(wù),就以非事務(wù)方式運(yùn)行 |
| PROPAGATION_MANDATORY | 支持當(dāng)前事務(wù),假設(shè)當(dāng)前沒有事務(wù),就拋出異常 |
| PROPAGATION_REQUIRES_NEW | 新建事務(wù),假設(shè)當(dāng)前存在事務(wù)。把當(dāng)前事務(wù)掛起 |
| PROPAGATION_NOT_SUPPORTED | 以非事務(wù)方式運(yùn)行操作。假設(shè)當(dāng)前存在事務(wù),就把當(dāng)前事務(wù)掛起 |
| PROPAGATION_NEVER | 以非事務(wù)方式運(yùn)行,假設(shè)當(dāng)前存在事務(wù),則拋出異常 |
| PROPAGATION_NESTED | 如果當(dāng)前存在事務(wù),則在嵌套事務(wù)內(nèi)執(zhí)行。如果當(dāng)前沒有事務(wù),則執(zhí)行與PROPAGATION_REQUIRED類似的操作。 |
事務(wù)隔離級(jí)別,事務(wù)的隔離級(jí)別只用4種但是spring提供有5種。(spring的隔離級(jí)別名字與數(shù)據(jù)庫(kù)中的不一樣)
| 隔離級(jí)別 | 說(shuō)明 | 臟讀 | 幻讀 | 不可重復(fù)讀 |
|---|---|---|---|---|
| ISOLATION_DEFAULT | 默認(rèn)隔離級(jí)別,每種數(shù)據(jù)庫(kù)支持的事務(wù)隔離級(jí)別不一樣,根據(jù)使用的數(shù)據(jù)庫(kù)改變。 | - | - | - |
| ISOLATION_READ_UNCOMMITTED | 讀未提交,即能夠讀取到?jīng)]有被提交的數(shù)據(jù)。 | 是 | 是 | 是 |
| ISOLATION_READ_COMMITTED | 讀已提交,即能夠讀到那些已經(jīng)提交的數(shù)據(jù)。 | 否 | 是 | 是 |
| ISOLATION_REPEATABLE_READ | 重復(fù)讀取,即在數(shù)據(jù)讀出來(lái)之后加鎖。這個(gè)事務(wù)不結(jié)束,別的事務(wù)無(wú)法操作這條數(shù)據(jù)。 | 否 | 否 | 是 |
| ISOLATION_SERIALIZABLE | 串行化,最高的事務(wù)隔離級(jí)別,不管多少事務(wù),挨個(gè)運(yùn)行完一個(gè)事務(wù)的所有子事務(wù)之后才可以執(zhí)行另外一個(gè)事務(wù)里面的所有子事務(wù)。 | 否 | 否 | 否 |
第三步
配置適配器(Advisor),增強(qiáng)事務(wù)。
創(chuàng)建適配器(一個(gè)普通的方法返回Advisor)。
Advisor(顧問)是由切入點(diǎn)和Advice(通知)組成的,但是Advisor是一個(gè)接口,需要實(shí)現(xiàn)它實(shí)現(xiàn)類DefaultPointcutAdvisor,并且傳入?yún)?shù)切入點(diǎn)和Adivce。
@Bean
public Advisor txAdviceAdvisor() {
//增強(qiáng)事務(wù),關(guān)聯(lián)切入點(diǎn)和事務(wù)屬性
return new DefaultPointcutAdvisor(切入點(diǎn), Advice);
}
配置切入點(diǎn)。
//配置切入點(diǎn)表達(dá)式 : 指定哪些包中的類使用事務(wù),設(shè)置為靜態(tài)類常量 private static final String AOP_POINTCUT_EXPRESSION = "execution (* com.***.service.*.*(..))"; //一下內(nèi)容放在適配器方法內(nèi) //配置事務(wù)切入點(diǎn)表達(dá)式 AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut(); pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
關(guān)聯(lián)切入點(diǎn)和Advice。
//增強(qiáng)事務(wù),關(guān)聯(lián)切入點(diǎn)和事務(wù)屬性 return new DefaultPointcutAdvisor(pointcut, txAdvice());
第四步
重啟測(cè)試!
最后奉上aop全局事務(wù)全部代碼
package com.gx.config;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionManager;
import org.springframework.transaction.interceptor.*;
import javax.sql.DataSource;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@Aspect //定義切面類,把當(dāng)前類標(biāo)識(shí)為一個(gè)切面供容器讀取
@Configuration //定義配置類
public class TransactionAdviceConfig {
//事務(wù)的超時(shí)時(shí)間為10秒
private static final int TX_METHOD_TIMEOUT = 10;
//配置切入點(diǎn)表達(dá)式 : 指定哪些包中的類使用事務(wù)
private static final String AOP_POINTCUT_EXPRESSION = "execution (* com.***.service.*.*(..))";
//事務(wù)管理器
@Autowired
private TransactionManager transactionManager;
/**
* 聲明業(yè)務(wù)方法的事務(wù)屬性
*/
@Bean
public TransactionInterceptor txAdvice() {
/**
* 這里配置只讀事務(wù)
*/
RuleBasedTransactionAttribute readOnlyTx = new RuleBasedTransactionAttribute();
readOnlyTx.setReadOnly(true);//是否只讀
readOnlyTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);//事務(wù)的傳播行為
/**
* 必須帶事務(wù)
* 當(dāng)前存在事務(wù)就使用當(dāng)前事務(wù),當(dāng)前不存在事務(wù),就開啟一個(gè)新的事務(wù)
*/
RuleBasedTransactionAttribute requiredTx = new RuleBasedTransactionAttribute();
//檢查型異常也回滾
requiredTx.setRollbackRules(
Collections.singletonList(new RollbackRuleAttribute(Exception.class)));
requiredTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
requiredTx.setTimeout(TX_METHOD_TIMEOUT);
/**
* 無(wú)事務(wù)地執(zhí)行,掛起任何存在的事務(wù)
*/
RuleBasedTransactionAttribute noTx = new RuleBasedTransactionAttribute();
noTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED);
/**
* 設(shè)置方法對(duì)應(yīng)的事務(wù)
*/
Map<String, TransactionAttribute> txMap = new HashMap<>();
//只讀事務(wù)
txMap.put("get*", readOnlyTx);
txMap.put("query*", readOnlyTx);
txMap.put("find*", readOnlyTx);
txMap.put("list*", readOnlyTx);
txMap.put("count*", readOnlyTx);
txMap.put("exist*", readOnlyTx);
txMap.put("search*", readOnlyTx);
txMap.put("fetch*", readOnlyTx);
//無(wú)事務(wù)
txMap.put("noTx*", noTx);
//寫事務(wù)
txMap.put("add*", requiredTx);
txMap.put("save*", requiredTx);
txMap.put("insert*", requiredTx);
txMap.put("update*", requiredTx);
txMap.put("modify*", requiredTx);
txMap.put("delete*", requiredTx);
NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
source.setNameMap(txMap);
//返回事務(wù)攔截器
return new TransactionInterceptor(transactionManager, source);
}
@Bean
public Advisor txAdviceAdvisor() {
//配置事務(wù)切入點(diǎn)表達(dá)式
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
//增強(qiáng)事務(wù),關(guān)聯(lián)切入點(diǎn)和事務(wù)屬性
return new DefaultPointcutAdvisor(pointcut, txAdvice());
}
}
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Freemaker Replace函數(shù)的正則表達(dá)式運(yùn)用
這篇文章主要介紹了Freemaker Replace函數(shù)的正則表達(dá)式運(yùn)用 的相關(guān)資料,需要的朋友可以參考下2015-12-12
解決IDEA2020.1.2IDEA打不開的問題(最新分享)
由于idea安裝多了某個(gè)jar,點(diǎn)擊出現(xiàn)讀條后閃退情況,接下來(lái)通過(guò)本文給大家分享解決IDEA2020.1.2IDEA打不開的問題,非常不錯(cuò),具有一定的參考借鑒價(jià)值,感興趣的朋友跟隨小編一起看看吧2020-07-07
java ClassLoader機(jī)制詳細(xì)講解
ClassLoader一個(gè)經(jīng)常出現(xiàn)又讓很多人望而卻步的詞,本文將試圖以最淺顯易懂的方式來(lái)講解 ClassLoader,希望能對(duì)不了解該機(jī)制的朋友起到一點(diǎn)點(diǎn)作用2016-07-07
Spring的事務(wù)控制實(shí)現(xiàn)方法
這篇文章主要為大家詳細(xì)介紹了Spring的事務(wù)控制實(shí)現(xiàn)方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-07-07
基于Java快速實(shí)現(xiàn)一個(gè)簡(jiǎn)單版的HashMap詳解
這篇文章主要為大家詳細(xì)介紹了如何利用Java簡(jiǎn)單實(shí)現(xiàn)一個(gè)底層數(shù)據(jù)結(jié)構(gòu)為數(shù)組?+?鏈表的HashMap,不考慮鏈表長(zhǎng)度超過(guò)8個(gè)時(shí)變?yōu)榧t黑樹的情況,需要的可以參考一下2023-02-02
JAVAWEB實(shí)現(xiàn)簡(jiǎn)單的商城項(xiàng)目(一)實(shí)例代碼解析
本文給大家分享一段實(shí)例代碼給大家介紹JAVAWEB實(shí)現(xiàn)簡(jiǎn)單的商城項(xiàng)目(一),非常具有參考價(jià)值,感興趣的朋友一起學(xué)習(xí)吧2016-02-02
使用SpringBoot開發(fā)Restful服務(wù)實(shí)現(xiàn)增刪改查功能
Spring Boot是由Pivotal團(tuán)隊(duì)提供的全新框架,其設(shè)計(jì)目的是用來(lái)簡(jiǎn)化新Spring應(yīng)用的初始搭建以及開發(fā)過(guò)程。這篇文章主要介紹了基于SpringBoot開發(fā)一個(gè)Restful服務(wù),實(shí)現(xiàn)增刪改查功能,需要的朋友可以參考下2018-01-01

