SpringBoot AOP如何配置全局事務(wù)
SpringBoot的出現(xiàn)使得項(xiàng)目中使用事務(wù)變得非常簡單,有兩種使用方式,適合小型項(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ù)屬性,并且注冊到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í)一般都是通過方法的名字篩選,比如add*、save*、delete*等,所以事務(wù)屬性使用的是他的子類NameMatchTransactionAttributeSource。
//事務(wù)管理器
@Autowired
private TransactionManager transactionManager;
@Bean
public TransactionInterceptor txAdvice() {
//聲明一個(gè)通過方法名字配置事務(wù)屬性的對(duì)象
NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
//返回一個(gè)事務(wù)攔截器
return new TransactionInterceptor(transactionManager, source);
}
通過方法名稱設(shè)置業(yè)務(wù)方法事務(wù)屬性。
NameMatchTransactionAttributeSource類中我們使用的頻繁的就兩個(gè)方法。
setNameMap其實(shí)就是addTransactionalMethod的集合。
//通過map集合給多個(gè)方法或者多類方法設(shè)置事務(wù)屬性
public void setNameMap(Map<String, TransactionAttribute> nameMap) {
nameMap.forEach(this::addTransactionalMethod);
}
//通過方法名稱或一類方法名稱和事務(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);
//通過方法名稱設(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ù)行為 | 說明 |
|---|---|
| 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ù)庫中的不一樣)
| 隔離級(jí)別 | 說明 | 臟讀 | 幻讀 | 不可重復(fù)讀 |
|---|---|---|---|---|
| ISOLATION_DEFAULT | 默認(rèn)隔離級(jí)別,每種數(shù)據(jù)庫支持的事務(wù)隔離級(jí)別不一樣,根據(jù)使用的數(shù)據(jù)庫改變。 | - | - | - |
| ISOLATION_READ_UNCOMMITTED | 讀未提交,即能夠讀取到?jīng)]有被提交的數(shù)據(jù)。 | 是 | 是 | 是 |
| ISOLATION_READ_COMMITTED | 讀已提交,即能夠讀到那些已經(jīng)提交的數(shù)據(jù)。 | 否 | 是 | 是 |
| ISOLATION_REPEATABLE_READ | 重復(fù)讀取,即在數(shù)據(jù)讀出來之后加鎖。這個(gè)事務(wù)不結(jié)束,別的事務(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());
第四步
重啟測試!
最后奉上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ù)地執(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ù)
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è)參考,也希望大家多多支持腳本之家。
- SpringBoot 攔截器 (Interceptor)與切面 (AOP)示例、作用及適用場景分析
- AOP在SpringBoot項(xiàng)目中的使用場景解讀
- SpringBoot整合Jasypt使用自定義注解+AOP實(shí)現(xiàn)敏感字段加解密
- Springboot如何正確使用AOP問題
- springboot接口服務(wù),防刷、防止請(qǐng)求攻擊,AOP實(shí)現(xiàn)方式
- SpringBoot3利用AOP實(shí)現(xiàn)IP黑名單功能
- springbootAOP定義切點(diǎn)獲取/修改請(qǐng)求參數(shù)方式
- SpringBoot實(shí)現(xiàn)AOP切面的三種方式
- SpringBoot中使用AOP實(shí)現(xiàn)日志記錄功能
- JAVA中Spring Boot的AOP切面編程是什么,如何使用?(實(shí)例代碼)
相關(guān)文章
SpringBoot中web模版數(shù)據(jù)渲染展示的案例詳解
憑借 Spring Framework 的模塊、與你最喜歡的工具的大量集成以及插入你自己的功能的能力,Thymeleaf 是現(xiàn)代 HTML5 JVM Web 開發(fā)的理想選擇——盡管它還有更多功能,本文重點(diǎn)給大家介紹SpringBoot中web模版數(shù)據(jù)渲染展示,需要的朋友可以參考下2022-01-01
Mybatis mapper接口動(dòng)態(tài)代理開發(fā)步驟解析
這篇文章主要介紹了Mybatis mapper接口動(dòng)態(tài)代理開發(fā)步驟解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07
SpringBoot實(shí)現(xiàn)quartz定時(shí)任務(wù)可視化管理功能
這篇文章主要介紹了SpringBoot實(shí)現(xiàn)quartz定時(shí)任務(wù)可視化管理功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-08-08
java一個(gè)接口多個(gè)實(shí)現(xiàn)類的調(diào)用方式
這篇文章主要給大家介紹了關(guān)于java一個(gè)接口多個(gè)實(shí)現(xiàn)類的調(diào)用方式的相關(guān)資料,經(jīng)測試確認(rèn),當(dāng)一個(gè)接口有多個(gè)實(shí)現(xiàn)時(shí),調(diào)用時(shí)只會(huì)執(zhí)行一個(gè),有時(shí)候需要多個(gè)實(shí)現(xiàn)調(diào)用,需要的朋友可以參考下2023-09-09
使用easyexcel導(dǎo)出的excel文件,使用poi讀取時(shí)異常處理方案
這篇文章主要介紹了使用easyexcel導(dǎo)出的excel文件,使用poi讀取時(shí)異常處理方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12
SpringBoot開發(fā)項(xiàng)目,引入JPA找不到findOne方法的解決
這篇文章主要介紹了SpringBoot開發(fā)項(xiàng)目,引入JPA找不到findOne方法的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11

