自己动手实现简单spring事物源码

本文演示了如何在非SpringBoot的Java项目中,利用AOP和jdbc进行事务管理。通过定义切点来拦截特定包下的所有类,然后在环绕通知中开启和提交事务,确保SQL操作在同一事务内执行。当出现异常时,事务会自动回滚。项目依赖包括Spring的相关模块和MySQL连接器。
摘要由CSDN通过智能技术生成

我们利用现有的AOP来实现事务的管理,数据库访问我们直接使用​​jdbc​​,首先明确两点:

切点应该如何定义?
通知要实现什么功能?
我们先说第一个问题,因为是我们自己模拟,所以关于切点的定义我们就设置的尽量简单一些,不妨就直接指定某个包下的所有类。对于第二个问题,我们也不做的过于复杂,在方法执行前开启事务,在方法执行后提交事务并关闭连接,所以我们需要定义一个环绕通知。同时,我们也需要将连接跟事务同步,保证事务中的所有SQL共用一个事务是实现事务管理的必要条件。基于此,我们开始编写代码

我们只需要引入Spring相关的依赖跟​​JDBC​​相关依赖即可,该项目仅仅是一个Spring环境下的Java项目,没有Web依赖,也不是SpringBoot项目,项目结构如下:

POM文件:

<?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.dmz.framework</groupId>
    <artifactId>mybatis</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.15</version>
        </dependency>
        
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
        
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.5</version>
        </dependency>
    </dependencies>
</project>

配置类:

// 开启AOP跟扫描组件即可
@EnableAspectJAutoProxy
@ComponentScan("com.dmz.mybatis.tx_demo")
public class Config {

}

完成事务管理的核心类:

public class TransactionUtil {

    public static final ThreadLocal<Connection> synchronousConnection =
            new ThreadLocal<Connection>();

    private TransactionUtil() {
    }

    public static Connection startTransaction() {
        Connection connection = synchronousConnection.get();
        if (connection == null) {
            try {
                // 这里替换成你自己的连接地址即可
                connection = DriverManager
                        .getConnection("jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8", "root", "123");
                synchronousConnection.set(connection);
                connection.setAutoCommit(false);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return connection;
    }

    public static int execute(String sql, Object... args) {
        Connection connection = startTransaction();
        try (PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
            if (args != null) {
                for (int i = 1; i < args.length + 1; i++) {
                    preparedStatement.setObject(i, args[i - 1]);
                }
            }
            return preparedStatement.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return 0;
    }

    public static void commit() {
        try (Connection connection = synchronousConnection.get()) {
            connection.commit();
            synchronousConnection.remove();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public static void rollback() {
        try (Connection connection = synchronousConnection.get()) {
            connection.rollback();
            synchronousConnection.remove();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

实际需要事务管理的类

@Component
public class UserService {
    public void saveUser() {
        TransactionUtil.execute
                ("INSERT INTO `test`.`user`(`id`, `name`) VALUES (?, ?)", 100, "dmz");
        // 测试回滚
        // throw new RuntimeException();
    }
}

切面:

@Component
@Aspect
public class TxAspect {

    @Pointcut("execution(public * com.dmz.mybatis.tx_demo.service..*.*(..))")
    private void pointcut() {
    }

    @Around("pointcut()")
    public Object around(JoinPoint joinPoint) throws Throwable {
        // 在方法执行前开启事务
        TransactionUtil.startTransaction();

        // 执行业务逻辑
        Object proceed = null;
        try {
            ProceedingJoinPoint method = (ProceedingJoinPoint) joinPoint;
            proceed = method.proceed();
        } catch (Throwable throwable) {
            // 出现异常进行回滚
            TransactionUtil.rollback();
            return proceed;
        }
        // 方法执行完成后提交事务
        TransactionUtil.commit();
        return proceed;
    }
}
用于测试的主函数:
public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ac =
                new AnnotationConfigApplicationContext(Config.class);
        UserService userService = ac.getBean(UserService.class);
        userService.saveUser();
    }
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值