SpringBoot AOP如何配置全局事务

 更新时间:2024年05月11日 11:44:03   作者:X爪哇程序猿  
这篇文章主要介绍了SpringBoot AOP如何配置全局事务问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

SpringBoot的出现使得项目中使用事务变得非常简单,有两种使用方式,适合小型项目的注解事务(声明式事务管理),适合大型项目的全局事务。

1、注解事务(次要)

注解事务使用只用两步,开启事务注解功能,使用事务注解功能,并且每步都只有使用一个注解。

第一步

开启事务注解功能@EnableTransactionManagement

在主启动类中添加注解@EnableTransactionManagement即可。

package com.gx;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@EnableTransactionManagement //开启事务注解功能
@SpringBootApplication
public class Ch09SpringbootTransAnnoApplication {

    public static void main(String[] args) {
        SpringApplication.run(Ch09SpringbootTransAnnoApplication.class, args);
    }

}

第二步

使用事务注解功能@Transactional

在service接口实现类或接口实现类方法上添加@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 //使用注解事务
    @Override
    public String addStudent(Student student) {
        //业务方法
    }
}

注意事项:@Transactional必须添加在public修饰的方法上。

2、全局事务(主要)

SpringBoot全局事务主要使用AOP切面编程。

第一步

创建切面类@Aspect

创建一个普通的类,加上@Aspect后该类就是一个切面类了,用于编写事务功能。

同时还需要把该切面类定义为一个配置类,添加注解@Configuration即可。

注意事项:

1、@Aspect将该类定义为切面类,把当前类作为一个切面被容器读取。

​ 2、@Configuration将该类定义为配置类,配置spring容器,注入bean

package com.gx.config;

import org.aspectj.lang.annotation.Aspect;
import org.springframework.context.annotation.Configuration;

@Aspect //定义切面类,把当前类标识为一个切面供容器读取
@Configuration //定义配置类
public class TransactionAdviceConfig {
	//增强方法
}

第二步

创建第一个方法,返回事务拦截器(TransactionInterceptor),声明业务方法的事务属性,并且注册到bean中。

需要返回事务拦截器TransactionInterceptor,就需要new一个TransactionInterceptor

根据TransactionInterceptor的类可得知,创建一个TransactionInterceptor目前只有两个方法。

public TransactionInterceptor() {
    
}

public TransactionInterceptor(TransactionManager ptm, TransactionAttributeSource tas) {
    this.setTransactionManager(ptm);
    this.setTransactionAttributeSource(tas);
}

要使用事务,就要有事务管理器TransactionManager和事务属性TransactionAttributeSource

配置事务属性时一般都是通过方法的名字筛选,比如add*save*delete*等,所以事务属性使用的是他的子类NameMatchTransactionAttributeSource

//事务管理器
@Autowired
private TransactionManager transactionManager;

@Bean
public TransactionInterceptor txAdvice() {
    //声明一个通过方法名字配置事务属性的对象
	NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
	//返回一个事务拦截器
    return new TransactionInterceptor(transactionManager, source);
}

通过方法名称设置业务方法事务属性。

NameMatchTransactionAttributeSource类中我们使用的频繁的就两个方法。

setNameMap其实就是addTransactionalMethod的集合。

//通过map集合给多个方法或者多类方法设置事务属性
public void setNameMap(Map<String, TransactionAttribute> nameMap) {
    nameMap.forEach(this::addTransactionalMethod);
}
//通过方法名称或一类方法名称和事务属性,给一个方法或一类方法设置事务属性
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);
}

设置事务属性。

TransactionAttribute:事务属性,有很多实现的实现类,一般使用基于规则的事务属性RuleBasedTransactionAttribute

功能大部分在他的父类DefaultTransactionDefinition中。

只写一小部分,其他可以根据业务需求写事务属性。

//配置一个事务属性(只读)
RuleBasedTransactionAttribute readOnlyTx = new RuleBasedTransactionAttribute();
//是否只读
readOnlyTx.setReadOnly(true);
//事务传播行为
readOnlyTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

//通过方法名称设置业务方法事务属性
Map<String, TransactionAttribute> txMap = new HashMap<>();
txMap.put("get*", readOnlyTx);

再添加到NameMatchTransactionAttributeSource

source.setNameMap(txMap);

设置事务属性的方法

  • 1、事务传播行为 setPropagationBehavior();
  • 2、事务隔离级别 setIsolationLevel();
  • 3、事务超时时间 setTimeout();
  • 4、事务只读 setReadOnly();
  • 5、设置事务名称 setName();
  • 6、设置回滚规则 setRollbackRules();

事务属性。(扩展)

事务传播行为

事务行为说明
PROPAGATION_REQUIRED支持当前事务,假设当前没有事务。就新建一个事务
PROPAGATION_SUPPORTS支持当前事务,假设当前没有事务,就以非事务方式运行
PROPAGATION_MANDATORY支持当前事务,假设当前没有事务,就抛出异常
PROPAGATION_REQUIRES_NEW新建事务,假设当前存在事务。把当前事务挂起
PROPAGATION_NOT_SUPPORTED以非事务方式运行操作。假设当前存在事务,就把当前事务挂起
PROPAGATION_NEVER以非事务方式运行,假设当前存在事务,则抛出异常
PROPAGATION_NESTED如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

事务隔离级别,事务的隔离级别只用4种但是spring提供有5种。(spring的隔离级别名字与数据库中的不一样)

隔离级别说明脏读幻读不可重复读
ISOLATION_DEFAULT默认隔离级别,每种数据库支持的事务隔离级别不一样,根据使用的数据库改变。---
ISOLATION_READ_UNCOMMITTED读未提交,即能够读取到没有被提交的数据。
ISOLATION_READ_COMMITTED读已提交,即能够读到那些已经提交的数据。
ISOLATION_REPEATABLE_READ重复读取,即在数据读出来之后加锁。这个事务不结束,别的事务无法操作这条数据。
ISOLATION_SERIALIZABLE串行化,最高的事务隔离级别,不管多少事务,挨个运行完一个事务的所有子事务之后才可以执行另外一个事务里面的所有子事务。

第三步

配置适配器(Advisor),增强事务。

创建适配器(一个普通的方法返回Advisor)。

Advisor(顾问)是由切入点和Advice(通知)组成的,但是Advisor是一个接口,需要实现它实现类DefaultPointcutAdvisor,并且传入参数切入点和Adivce。

@Bean
public Advisor txAdviceAdvisor() {
    //增强事务,关联切入点和事务属性
    return new DefaultPointcutAdvisor(切入点, Advice);
}

配置切入点。

//配置切入点表达式 : 指定哪些包中的类使用事务,设置为静态类常量
private static final String AOP_POINTCUT_EXPRESSION = "execution (* com.***.service.*.*(..))";

//一下内容放在适配器方法内
//配置事务切入点表达式
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(AOP_POINTCUT_EXPRESSION);

关联切入点和Advice。

//增强事务,关联切入点和事务属性
return new DefaultPointcutAdvisor(pointcut, txAdvice());

第四步

重启测试!

最后奉上aop全局事务全部代码

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 //定义切面类,把当前类标识为一个切面供容器读取
@Configuration //定义配置类
public class TransactionAdviceConfig {

    //事务的超时时间为10秒
    private static final int TX_METHOD_TIMEOUT = 10;

    //配置切入点表达式 : 指定哪些包中的类使用事务
    private static final String AOP_POINTCUT_EXPRESSION = "execution (* com.***.service.*.*(..))";

    //事务管理器
    @Autowired
    private TransactionManager transactionManager;
    /**
     * 声明业务方法的事务属性
     */
    @Bean
    public TransactionInterceptor txAdvice() {

        /**
         * 这里配置只读事务
         */
        RuleBasedTransactionAttribute readOnlyTx = new RuleBasedTransactionAttribute();
        readOnlyTx.setReadOnly(true);//是否只读
        readOnlyTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);//事务的传播行为

        /**
         * 必须带事务
         * 当前存在事务就使用当前事务,当前不存在事务,就开启一个新的事务
         */
        RuleBasedTransactionAttribute requiredTx = new RuleBasedTransactionAttribute();
        //检查型异常也回滚
        requiredTx.setRollbackRules(
                Collections.singletonList(new RollbackRuleAttribute(Exception.class)));
        requiredTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        requiredTx.setTimeout(TX_METHOD_TIMEOUT);

        /**
         * 无事务地执行,挂起任何存在的事务
         */
        RuleBasedTransactionAttribute noTx = new RuleBasedTransactionAttribute();
        noTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED);

        /**
         * 设置方法对应的事务
         */
        Map<String, TransactionAttribute> txMap = new HashMap<>();
        //只读事务
        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);
        //无事务
        txMap.put("noTx*", noTx);
        //写事务
        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);
        
		//返回事务拦截器
        return new TransactionInterceptor(transactionManager, source);
    }

    @Bean
    public Advisor txAdviceAdvisor() {
        //配置事务切入点表达式
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
        //增强事务,关联切入点和事务属性
        return new DefaultPointcutAdvisor(pointcut, txAdvice());
    }
}

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Java NIO Channel 使用详情

    Java NIO Channel 使用详情

    这篇文章主要介绍了Java NIO Channel 使用详情,文章围绕主题展开详细内容需要的小伙伴可以参考一下,希望对你的学习有所帮助
    2022-04-04
  • Freemaker Replace函数的正则表达式运用

    Freemaker Replace函数的正则表达式运用

    这篇文章主要介绍了Freemaker Replace函数的正则表达式运用 的相关资料,需要的朋友可以参考下
    2015-12-12
  • 解决IDEA2020.1.2IDEA打不开的问题(最新分享)

    解决IDEA2020.1.2IDEA打不开的问题(最新分享)

    由于idea安装多了某个jar,点击出现读条后闪退情况,接下来通过本文给大家分享解决IDEA2020.1.2IDEA打不开的问题,非常不错,具有一定的参考借鉴价值,感兴趣的朋友跟随小编一起看看吧
    2020-07-07
  • java ClassLoader机制详细讲解

    java ClassLoader机制详细讲解

    ClassLoader一个经常出现又让很多人望而却步的词,本文将试图以最浅显易懂的方式来讲解 ClassLoader,希望能对不了解该机制的朋友起到一点点作用
    2016-07-07
  • Spring的事务控制实现方法

    Spring的事务控制实现方法

    这篇文章主要为大家详细介绍了Spring的事务控制实现方法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-07-07
  • 基于Java快速实现一个简单版的HashMap详解

    基于Java快速实现一个简单版的HashMap详解

    这篇文章主要为大家详细介绍了如何利用Java简单实现一个底层数据结构为数组 + 链表的HashMap,不考虑链表长度超过8个时变为红黑树的情况,需要的可以参考一下
    2023-02-02
  • Spring Bean生命周期之Bean的注册详解

    Spring Bean生命周期之Bean的注册详解

    这篇文章主要为大家详细介绍了Spring Bean生命周期之Bean的注册,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-03-03
  • Java常见的四种负载均衡算法

    Java常见的四种负载均衡算法

    本文主要介绍了Java常见的四种负载均衡算法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-05-05
  • JAVAWEB实现简单的商城项目(一)实例代码解析

    JAVAWEB实现简单的商城项目(一)实例代码解析

    本文给大家分享一段实例代码给大家介绍JAVAWEB实现简单的商城项目(一),非常具有参考价值,感兴趣的朋友一起学习吧
    2016-02-02
  • 使用SpringBoot开发Restful服务实现增删改查功能

    使用SpringBoot开发Restful服务实现增删改查功能

    Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。这篇文章主要介绍了基于SpringBoot开发一个Restful服务,实现增删改查功能,需要的朋友可以参考下
    2018-01-01

最新评论