用spring-boot-starter实现事务的统一配置

一、前言

微服务架构下,多个微服务都需要事务操作,如果在每个微服务下都从头配置事务,将非常繁锁。事务配置具有高度的一致性,可以抽取出来,制作starter,在需要配置事务的服务中引入starter依赖即可。

二、制作starter

1、完整结构图

在这里插入图片描述

2、引用模块

名称:tuwer-transaction-spring-boot-starter

引用模块用于外部引用。只有pom.xml文件

  • pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.tuwer</groupId>
    <artifactId>tuwer-transaction-spring-boot-starter</artifactId>
    <version>1.0-SNAPSHOT</version>
    <description>事务starter</description>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <!-- 编译编码 -->
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <!-- 自动配置模块 -->
        <dependency>
            <groupId>com.tuwer</groupId>
            <artifactId>tuwer-transaction-spring-boot-starter-autoconfigure</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>

3、自动配置模块

名称:tuwer-transaction-spring-boot-starter-autoconfigure\

1> pom.xml

使用jdbc的事务管理器

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.7</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.tuwer</groupId>
    <artifactId>tuwer-transaction-spring-boot-starter-autoconfigure</artifactId>
    <version>1.0-SNAPSHOT</version>
    <description>事务starter自动配置模块</description>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <!-- 编译编码 -->
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <!-- 基础启动器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <!-- aop -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <!-- 数据库 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
    </dependencies>
</project>

2> 切入点属性类

  • 用于外部配置切入点

  • 可以配置多个

  • 如果没有配置,就使用默认的:execution(* com.tuwer.service..*Impl.*(..)),【com.tuwer.service】包及子包下,所有以【Impl】结尾的类的所有方法

package com.tuwer.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.util.CollectionUtils;

import java.util.HashSet;
import java.util.Set;

/**
 * <p>事务属性类</p>
 *
 * @author 土味儿
 * Date 2023/4/17
 * @version 1.0
 */
@ConfigurationProperties(prefix = "tuwer-transaction")
public class TransactionProperty {
    /**
     * 切入点(可以有多个)
     * 格式必须符合要求
     */
    private Set<String> pointcut = new HashSet<>();

    /**
     * 获取切入点表达式(可以有多个组合)
     * -----------------------------------
     * execution(...) || execution(...)
     * -----------------------------------
     * @return
     */
    public String getPointcutExpression() {
        // 如果set集合为null,或没有值时,使用默认值
        if(CollectionUtils.isEmpty(this.pointcut)){
            // 默认切入点:【com.tuwer.service】包及子包下,所有以【Impl】结尾的类的所有方法
            return "execution(* com.tuwer.service..*Impl.*(..))";
        }

        StringBuilder sb = new StringBuilder();
        // 组合多个切入点
        for (String p : this.pointcut) {
            sb.append(" || ").append("execution(").append(p).append(")");
        }

        // 组合后,去除最前面的" || "返回
        return sb.substring(3);
    }

    public void setPointcut(Set<String> pointcut) {
         this.pointcut = pointcut;
    }
}

外部配置切入点示例:

由于切入点中有特殊字符*,所以需要加入引号:单引号或双引号都可以

# 事务切入点
tuwer-transaction:
  pointcut:
    - '* com.tuwer1231.service..*Impl.*(..))'
    - '* com.tuwer.service123..*Impl.*(..))'
    - '* com.tuwer345.service123..*Impl.*(..))'

3> 切面自动配置类

package com.tuwer.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.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionManager;
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
import org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource;
import org.springframework.transaction.interceptor.TransactionInterceptor;

import javax.annotation.Resource;

/**
 * <p>事务自动配置类</p>
 *
 * @author 土味儿
 * Date 2023/4/17
 * @version 1.0
 */
@Aspect
@Configuration
@EnableConfigurationProperties(TransactionProperty.class)
public class TuwerTransactionAutoConfiguration {
    /**
     * 注入 TransactionProperty 属性配置类
     */
    @Resource
    private TransactionProperty transactionProperty;

    /**
     * 注入事务管理器
     */
    @Resource
    private TransactionManager transactionManager;

    /**
     * 定义事务增强
     *
     * @return
     */
    @Bean
    public TransactionInterceptor txAdvice() {
        NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
        // 查询等;只读
        DefaultTransactionAttribute readonlyAttr = new DefaultTransactionAttribute();
        readonlyAttr.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        readonlyAttr.setReadOnly(true);

        source.addTransactionalMethod("get*", readonlyAttr);
        source.addTransactionalMethod("select*", readonlyAttr);
        source.addTransactionalMethod("query*", readonlyAttr);
        source.addTransactionalMethod("load*", readonlyAttr);
        source.addTransactionalMethod("search*", readonlyAttr);
        source.addTransactionalMethod("find*", readonlyAttr);
        source.addTransactionalMethod("list*", readonlyAttr);
        source.addTransactionalMethod("count*", readonlyAttr);
        source.addTransactionalMethod("is*", readonlyAttr);

        // 增删改
        DefaultTransactionAttribute otherAttr = new DefaultTransactionAttribute();
        otherAttr.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

        source.addTransactionalMethod("*", otherAttr);

        return new TransactionInterceptor(transactionManager, source);
    }

    /**
     * 织入事务
     *
     * @return
     */
    @Bean
    public Advisor txAdviceAdvisor() {
        // 切入点
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression(transactionProperty.getPointcutExpression());

        return new DefaultPointcutAdvisor(pointcut, txAdvice());
    }
}

4> spring.factories

指明自动配置类的地址,在 resources 目录下编写一个自己的 META-INF\spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.tuwer.config.TuwerTransactionAutoConfiguration

5> Install

把starter安装install到本地maven仓库中

三、使用说明

1、引入starter依赖

<!-- 事务starter -->
<dependency>
	<groupId>com.tuwer</groupId>
	<artifactId>tuwer-transaction-spring-boot-starter</artifactId>
	<version>1.0-SNAPSHOT</version>
</dependency>

2、配置切入点

如果不配置,就使用默认的切入点;此步骤可以省略。

至此,事务配置就OK了!

### Spring Boot集成`dynamic-datasource-spring-boot-starter`的事务配置 当使用 `dynamic-datasource-spring-boot-starter` 进行多数据源切换时,为了确保跨多个数据库操作的一致性和可靠性,正确设置事务管理至关重要。默认情况下,Spring 的声明式事务管理器 (`@Transactional`) 只能作用于单一的数据源上。 对于多数据源场景下的事务处理,可以采用以下几种方式来实现[^1]: #### 使用AOP切面编程控制事务传播行为 通过自定义 AOP 切入点,在方法执行前后动态调整当前线程绑定的数据源上下文,并根据业务逻辑需求指定不同的事务隔离级别和传播特性。这种方式灵活性较高,适用于复杂业务流程中的细粒度控制。 ```java import org.aspectj.lang.annotation.Aspect; import com.baomidou.dynamic.datasource.annotation.DS; @Aspect public class DataSourceTransactionAspect { @Around("@annotation(DS)") public Object around(ProceedingJoinPoint point) throws Throwable { try { // 设置目标DS前缀... String result = (String)point.proceed(); // 清理DS后置处理... return result; } catch (Throwable ex){ throw ex; } } } ``` #### 统一配置全局事务管理策略 如果应用程序内所有的服务调用都遵循相同的事务规则,则可以在应用启动类或配置文件中统一设定默认的事务属性,简化开发工作量的同时也提高了系统的可维护性。 ```yaml spring: transaction: default-timeout: 30s isolation-level-for-create: DEFAULT propagation-behavior-name: REQUIRED ``` #### 常见问题及解决方案 - **无法回滚分布式事务** 当涉及两个及以上不同数据库之间的交互时,可能会遇到提交失败但未能成功回滚的情况。此时建议引入支持XA协议的JTA(Java Transaction API),借助 Atomikos 或 Bitronix 等开源组件完成强一致性保障[^2]。 - **读写分离模式下主从库同步延迟引发脏读现象** 对于高并发访问的应用来说,通常会部署一套主备架构以提高可用性和性能表现;然而这也会带来一定的挑战——即由于网络传输等因素造成副本间存在短暂的时间差。针对此类情况,一方面要优化SQL语句减少不必要的锁竞争,另一方面则可通过增加版本号字段辅助判断记录状态变化从而规避潜在风险。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

土味儿~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值