从01开始 从01开始
首页
  • 计算机科学导论
  • 数字电路
  • 计算机组成原理

    • 计算机组成原理-北大网课
  • 操作系统
  • Linux
  • Docker
  • 计算机网络
  • 计算机常识
  • Git
  • JavaSE
  • Java高级
  • JavaEE

    • Ant
    • Maven
    • Log4j
    • Junit
    • JDBC
    • XML-JSON
  • JavaWeb

    • 服务器软件
    • Servlet
  • Spring
  • 主流框架

    • Redis
    • Mybatis
    • Lucene
    • Elasticsearch
    • RabbitMQ
    • MyCat
    • Lombok
  • SpringMVC
  • SpringBoot
  • 学习网课的心得
  • 输入法
  • 节假日TodoList
  • 其他
  • 关于本站
  • 网站日记
  • 友人帐
  • 如何搭建一个博客
GitHub (opens new window)

peterjxl

人生如逆旅,我亦是行人
首页
  • 计算机科学导论
  • 数字电路
  • 计算机组成原理

    • 计算机组成原理-北大网课
  • 操作系统
  • Linux
  • Docker
  • 计算机网络
  • 计算机常识
  • Git
  • JavaSE
  • Java高级
  • JavaEE

    • Ant
    • Maven
    • Log4j
    • Junit
    • JDBC
    • XML-JSON
  • JavaWeb

    • 服务器软件
    • Servlet
  • Spring
  • 主流框架

    • Redis
    • Mybatis
    • Lucene
    • Elasticsearch
    • RabbitMQ
    • MyCat
    • Lombok
  • SpringMVC
  • SpringBoot
  • 学习网课的心得
  • 输入法
  • 节假日TodoList
  • 其他
  • 关于本站
  • 网站日记
  • 友人帐
  • 如何搭建一个博客
GitHub (opens new window)
  • JavaSE

  • JavaSenior

  • JavaEE

  • JavaWeb

  • Spring

    • Spring介绍
    • 程序中的耦合
    • IoC的概念和作用
    • Spring中的依赖注入
    • 基于注解的IoC
    • 使用基于XML的IoC完成单表的CRUD
    • 使用基于注解的IoC完成单表的CRUD
    • IoC的纯注解配置
    • Spring整合Junit
    • 事务问题
    • 代理模式
    • AOP的概念和入门
    • 基于注解的AOP
    • Spring的JdbcTemplate
    • JdBCDaoSupport
    • 基于XML的AOP实现事务控制
    • 基于注解的AOP实现事务控制
      • 配置bean.xml
      • 修改service类
      • 配置dao
      • 配置ConnectionUtils
      • 配置​TransactionManager​​
      • 测试
      • 使用环绕通知
      • 源码
    • Spring的事务控制
    • 基于XML的声明式事务控制
    • 基于注解的声明式事务控制
    • 纯注解实现事务控制
    • Spring编程式事务控制
    • Spring5新特性
    • Java
  • 主流框架

  • SpringMVC

  • SpringBoot

  • Java并发

  • Java源码

  • JVM

  • 韩顺平

  • Java
  • Java
  • Spring
2023-05-08
目录

基于注解的AOP实现事务控制

# 130.基于注解的AOP实现事务控制

我们改造上一篇博客的案例,改为使用注解的方式  ‍

‍

# 配置bean.xml

由于我们使用了注解,因此得加上注解的约束:添加了第5,10,11行

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

1
2
3
4
5
6
7
8
9
10
11
12

‍

‍

‍

我们首先配置容器创建时要扫描的包:

<context:component-scan base-package="com.peterjxl"/>
1

‍

‍

‍

# 修改service类

在service实现类中,加上@Service的注解,并给dao加上@Autowired​注解,去掉set方法

@Service("accountService")
public class AccountServiceImpl implements IAccountService {

    @Autowired
    private IAccountDao accountDao;
1
2
3
4
5

然后我们删除bean.xml中的配置

‍

# 配置dao

dao实现类同理,加上@Repository注解,并给成员变量加上@Autowired​注解,去掉set方法

@Repository("accountDao")
public class AccountDaoImpl implements IAccountDao {

    @Autowired
    private QueryRunner runner;

    @Autowired
    private ConnectionUtils connectionUtils;
1
2
3
4
5
6
7
8

然后我们删除bean.xml中的配置

‍

‍

# 配置ConnectionUtils

加上@Compoment注解,并给dataSource加上注解,去掉set方法

@Component("connectionUtils")
public class ConnectionUtils {

    private ThreadLocal<Connection> tl = new ThreadLocal<Connection>();

    @Autowired
    private DataSource dataSource;
1
2
3
4
5
6
7

然后我们删除bean.xml中的配置

‍

# 配置​TransactionManager​​

我们做如下事情

  1. 给类加上Component的注解,和@Aspect的注解
  2. 配置切入点表达式
  3. 给各个方法配置通知

完整代码:

package com.peterjxl.utils;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;


@Component("transactionManager")
@Aspect
public class TransactionManager {

    @Autowired
    private ConnectionUtils connectionUtils;

    @Pointcut("execution(* com.peterjxl.service.impl.*.*(..))")
    private void pt1() {}


    @Before("pt1()")
    public void beginTransaction() {
        try {
            connectionUtils.getThreadConnection().setAutoCommit(false);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    @AfterReturning("pt1()")
    public void commit() {
        try {
            connectionUtils.getThreadConnection().commit();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @AfterThrowing("pt1()")
    public void rollback() {
        try {
            connectionUtils.getThreadConnection().rollback();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @After("pt1()")
    public void release() {
        try {
            connectionUtils.getThreadConnection().close(); // 还回连接池中
            connectionUtils.removeConnection();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

‍

‍

配置bean.xml,我们删除其bean配置,和AOP的配置,然后加上

 <aop:aspectj-autoproxy/>
1

‍

‍

# 测试

此时我们就已经用注解改造完了,完整的配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
  
    <context:component-scan base-package="com.peterjxl"/>
    <aop:aspectj-autoproxy/>

    <!--配置QueryRunner-->
    <bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype"/>

    <!-- 配置数据源 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!--连接数据库的必备信息-->
        <property name="driverClass" value="com.mysql.cj.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/learnSpring"/>
        <property name="user" value="learnSpringUser"/>
        <property name="password" value="learnSpringPassword"/>
    </bean>
</beans>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

‍

然后我们测试下,有异常的情况下能否正常运行,运行结果:

java.lang.RuntimeException: java.sql.SQLNonTransientConnectionException: Can't call rollback when autocommit=true
1

‍

这是因为使用注解的时候,执行顺序是有问题的,有异常时先执行了最终通知,也就是执行了release方法释放了连接,然后再回滚,是会出错的

即使没有异常,提交也是不行的,因为提交的时候先执行了最终通知,释放了连接,然后commit,当然也是不行的

综上,不管转账时有无异常,数据库数据都没有变化,因为既没有提交,也没有回滚。只能用环绕通知

‍

# 使用环绕通知

我们将其他方法上面的注解注释掉,然后在​TransactionManager​新加一个方法:

@Around("pt1()")
public Object aroundAdvice(ProceedingJoinPoint pjp) {
    Object rtValue = null;
    try {
        // 1. 获取参数
        Object[] args = pjp.getArgs();
        // 2. 开启事务
        this.beginTransaction();
        // 3. 执行方法
        rtValue = pjp.proceed(args);
        // 4. 提交事务
        this.commit();
        // 5. 返回结果
        return rtValue;
    } catch (Throwable e) {
        // 6. 回滚事务
        this.rollback();
        throw new RuntimeException(e);
    } finally {
        // 7. 释放资源
        this.release();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

‍

此时我们再进行测试,能看到正常控制事务

‍

‍

# 源码

本项目已将源码上传到GitHub (opens new window)和Gitee (opens new window)上。并且创建了分支demo16,读者可以通过切换分支来查看本文的示例代码

在GitHub上编辑此页 (opens new window)
上次更新: 2023/6/7 11:49:40
基于XML的AOP实现事务控制
Spring的事务控制

← 基于XML的AOP实现事务控制 Spring的事务控制→

Theme by Vdoing | Copyright © 2022-2023 粤ICP备2022067627号-1 粤公网安备 44011302003646号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式