用Spring AOP(面向切面编程)编写简单转账功能实例:

代码结构图

1.准备数据库存储数据(在MySQL中编写)

 1 # 删除spring_aop数据库
2 drop database if exists spring_aop;
3
4 # 创建spring_aop数据库
5 create database spring_aop;
6
7 # 使用spring_aop数据库
8 use spring_aop;
9
10 # 创建account表
11 create table account (
12 id int(11) auto_increment primary key,
13 accountNum varchar(20) default NULL,
14 money int(8) default 0
15 );
16
17 # 新增数据
18 insert into account (accountNum, money) values
19 ("622200001",1000),("622200002",1000);

2.导入Spring基础包(pop.xml)

 1 <?xml version="1.0" encoding="UTF-8"?>
2 <project xmlns="http://maven.apache.org/POM/4.0.0"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5 <modelVersion>4.0.0</modelVersion>
6
7 <groupId>org.example</groupId>
8 <artifactId>spring-aop-zhuangshuhui</artifactId>
9 <version>1.0-SNAPSHOT</version>
10
11 <dependencies>
12 <!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
13 <dependency>
14 <groupId>org.springframework</groupId>
15 <artifactId>spring-core</artifactId>
16 <version>5.2.13.RELEASE</version>
17 </dependency>
18 <!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
19 <dependency>
20 <groupId>org.springframework</groupId>
21 <artifactId>spring-beans</artifactId>
22 <version>5.2.13.RELEASE</version>
23 </dependency>
24 <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
25 <dependency>
26 <groupId>org.springframework</groupId>
27 <artifactId>spring-context</artifactId>
28 <version>5.2.13.RELEASE</version>
29 </dependency>
30 <!-- https://mvnrepository.com/artifact/org.springframework/spring-expression -->
31 <dependency>
32 <groupId>org.springframework</groupId>
33 <artifactId>spring-expression</artifactId>
34 <version>5.2.13.RELEASE</version>
35 </dependency>
36 <!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
37 <dependency>
38 <groupId>org.springframework</groupId>
39 <artifactId>spring-aop</artifactId>
40 <version>5.2.13.RELEASE</version>
41 </dependency>
42 <!-- https://mvnrepository.com/artifact/org.springframework/spring-jcl -->
43 <dependency>
44 <groupId>org.springframework</groupId>
45 <artifactId>spring-jcl</artifactId>
46 <version>5.2.13.RELEASE</version>
47 </dependency>
48 <!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
49 <dependency>
50 <groupId>org.springframework</groupId>
51 <artifactId>spring-test</artifactId>
52 <version>5.2.13.RELEASE</version>
53 <scope>test</scope>
54 </dependency>
55 <!-- https://mvnrepository.com/artifact/commons-dbutils/commons-dbutils -->
56 <dependency>
57 <groupId>commons-dbutils</groupId>
58 <artifactId>commons-dbutils</artifactId>
59 <version>1.7</version>
60 </dependency>
61 <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
62 <dependency>
63 <groupId>mysql</groupId>
64 <artifactId>mysql-connector-java</artifactId>
65 <version>8.0.23</version>
66 </dependency>
67 <!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
68 <dependency>
69 <groupId>com.mchange</groupId>
70 <artifactId>c3p0</artifactId>
71 <version>0.9.5.5</version>
72 </dependency>
73 <!-- https://mvnrepository.com/artifact/junit/junit -->
74 <dependency>
75 <groupId>junit</groupId>
76 <artifactId>junit</artifactId>
77 <version>4.13.2</version>
78 <scope>test</scope>
79 </dependency>
80 <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
81 <dependency>
82 <groupId>org.aspectj</groupId>
83 <artifactId>aspectjweaver</artifactId>
84 <version>1.9.3</version>
85 </dependency>
86 </dependencies>
87
88
89 </project>

3.核心配置文件(applicationContext.xml)

 1 <?xml version="1.0" encoding="UTF-8"?>
2 <project xmlns="http://maven.apache.org/POM/4.0.0"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5 <modelVersion>4.0.0</modelVersion>
6
7 <groupId>org.example</groupId>
8 <artifactId>spring-aop-zhuangshuhui</artifactId>
9 <version>1.0-SNAPSHOT</version>
10
11 <dependencies>
12 <!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
13 <dependency>
14 <groupId>org.springframework</groupId>
15 <artifactId>spring-core</artifactId>
16 <version>5.2.13.RELEASE</version>
17 </dependency>
18 <!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
19 <dependency>
20 <groupId>org.springframework</groupId>
21 <artifactId>spring-beans</artifactId>
22 <version>5.2.13.RELEASE</version>
23 </dependency>
24 <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
25 <dependency>
26 <groupId>org.springframework</groupId>
27 <artifactId>spring-context</artifactId>
28 <version>5.2.13.RELEASE</version>
29 </dependency>
30 <!-- https://mvnrepository.com/artifact/org.springframework/spring-expression -->
31 <dependency>
32 <groupId>org.springframework</groupId>
33 <artifactId>spring-expression</artifactId>
34 <version>5.2.13.RELEASE</version>
35 </dependency>
36 <!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
37 <dependency>
38 <groupId>org.springframework</groupId>
39 <artifactId>spring-aop</artifactId>
40 <version>5.2.13.RELEASE</version>
41 </dependency>
42 <!-- https://mvnrepository.com/artifact/org.springframework/spring-jcl -->
43 <dependency>
44 <groupId>org.springframework</groupId>
45 <artifactId>spring-jcl</artifactId>
46 <version>5.2.13.RELEASE</version>
47 </dependency>
48 <!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
49 <dependency>
50 <groupId>org.springframework</groupId>
51 <artifactId>spring-test</artifactId>
52 <version>5.2.13.RELEASE</version>
53 <scope>test</scope>
54 </dependency>
55 <!-- https://mvnrepository.com/artifact/commons-dbutils/commons-dbutils -->
56 <dependency>
57 <groupId>commons-dbutils</groupId>
58 <artifactId>commons-dbutils</artifactId>
59 <version>1.7</version>
60 </dependency>
61 <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
62 <dependency>
63 <groupId>mysql</groupId>
64 <artifactId>mysql-connector-java</artifactId>
65 <version>8.0.23</version>
66 </dependency>
67 <!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
68 <dependency>
69 <groupId>com.mchange</groupId>
70 <artifactId>c3p0</artifactId>
71 <version>0.9.5.5</version>
72 </dependency>
73 <!-- https://mvnrepository.com/artifact/junit/junit -->
74 <dependency>
75 <groupId>junit</groupId>
76 <artifactId>junit</artifactId>
77 <version>4.13.2</version>
78 <scope>test</scope>
79 </dependency>
80 <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
81 <dependency>
82 <groupId>org.aspectj</groupId>
83 <artifactId>aspectjweaver</artifactId>
84 <version>1.9.3</version>
85 </dependency>
86 </dependencies>
87
88
89 </project>

代码编写:

ConnectionUtils.java  (连接数据库)

 1 package utils;
2
3 import com.mchange.v2.c3p0.ComboPooledDataSource;
4 import org.springframework.beans.factory.annotation.Autowired;
5 import org.springframework.stereotype.Component;
6
7 import java.sql.Connection;
8 import java.sql.SQLException;
9
10 @Component
11 public class ConnectionUtils {
12 private ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
13 @Autowired
14 private ComboPooledDataSource dataSource;
15
16 /**
17 * 获得当前线程绑定的连接
18 *
19 * @return
20 */
21 public Connection getThreadConnection() {
22 try {
23 // 看线程是否绑了连接
24 Connection conn = tl.get();
25 if (conn == null) {
26 // 从数据源获取一个连接
27 conn = dataSource.getConnection();
28 // 和线程局部变量 绑定
29 tl.set(conn);
30 }
31 // 返回线程连接
32 return tl.get();
33 } catch (SQLException e) {
34 throw new RuntimeException(e);
35 }
36 }
37
38 /**
39 * 把连接和当前线程进行解绑
40 */
41 public void remove() {
42 tl.remove();
43 }
44
45 public void removeConnection() {
46 tl.remove();
47 }
48 }

Account.java (实体类)

 1 package entity;
2
3 public class Account {
4 private Integer id;
5 private String accountNum;
6 private Integer money;
7
8 public String getAccountNum() {
9 return accountNum;
10 }
11
12 public Integer getMoney() {
13 return money;
14 }
15
16 public void setMoney(Integer money){
17 this.money = money;
18 }
19
20 public Integer getId() {
21 return id;
22 }
23 public void setId(Integer id){
24 this.id = id;
25 }
26 public void setAccountNum(String accountNum){
27 this.accountNum = accountNum;
28 }
29 }

AccountDao.java  (Dao层)

 1 package dao;
2
3 import entity.Account;
4
5 public interface AccountDao {
6 /**
7 * 更新
8 *
9 * @param account
10 */
11 void updateAccount(Account account);
12
13 /**
14 * 根据编号查询账户
15 *
16 * @param accountNum
17 * @return 如果没有结果就返回null,如果结果集超过一个就抛异常,如果有唯一的一个结果就返回
18 */
19 Account findAccountByNum(String accountNum);
20 }

AccountDaoImpl.java  (Dao层实现类)

 1 package dao.impl;
2
3 import dao.AccountDao;
4 import entity.Account;
5 import org.apache.commons.dbutils.QueryRunner;
6 import org.apache.commons.dbutils.handlers.BeanListHandler;
7 import org.springframework.beans.factory.annotation.Autowired;
8 import org.springframework.stereotype.Repository;
9 import utils.ConnectionUtils;
10
11 import java.sql.SQLException;
12 import java.util.List;
13
14 @Repository("accountDao")
15 public class AccountDaoImpl implements AccountDao {
16 // 数据库查询工具类
17 @Autowired
18 private QueryRunner runner;
19 // 数据库连接工具类
20 @Autowired
21 private ConnectionUtils connectionUtils;
22
23 /**
24 * 更新
25 *
26 * @param account
27 */
28 public void updateAccount(Account account) {
29 try {
30 runner.update(connectionUtils.getThreadConnection(),
31 "update account set accountNum=?,money=? where id=?",
32 account.getAccountNum(), account.getMoney(), account.getId());
33 } catch (SQLException e) {
34 throw new RuntimeException(e);
35 }
36 }
37
38 /**
39 * 根据编号查询账户
40 *
41 * @param accountNum
42 * @return 如果没有结果就返回null,如果结果集超过一个就抛异常,如果有唯一的一个结果就返回
43 */
44 public Account findAccountByNum(String accountNum) {
45 List<Account> accounts = null;
46 try {
47 accounts = runner.query(connectionUtils.getThreadConnection(),
48 "select * from account where accountNum = ? ",
49 new BeanListHandler<Account>(Account.class),
50 accountNum);
51 } catch (SQLException e) {
52 throw new RuntimeException(e);
53 }
54 if (accounts == null || accounts.size() == 0) {
55 // 如果没有结果就返回null
56 return null;
57 } else if (accounts.size() > 1) {
58 // 如果结果集超过一个就抛异常
59 throw new RuntimeException("结果集不唯一,数据有问题");
60 } else {
61 // 如果有唯一的一个结果就返回
62 return accounts.get(0);
63 }
64 }
65 }

AccountService.java (业务层)

 1 package services;
2
3 public interface AccountService {
4 /**
5 * 转账
6 *
7 * @param sourceAccount 转出账户
8 * @param targetAccount 转入账户
9 * @param money 转账金额
10 */
11 void transfer(String sourceAccount, String targetAccount, Integer money);
12 }

AccountServiceImpl.java (业务层实现类)

 1 package services.impl;
2
3 import dao.AccountDao;
4 import entity.Account;
5 import org.springframework.beans.factory.annotation.Autowired;
6 import org.springframework.stereotype.Service;
7 import services.AccountService;
8
9 @Service("accountService")
10 public class AccountServiceImpl implements AccountService {
11
12 @Autowired
13 private AccountDao accountDao;
14
15 /**
16 * 转账
17 *
18 * @param sourceAccount 转出账户
19 * @param targetAccount 转入账户
20 * @param money 转账金额
21 */
22 public void transfer(String sourceAccount, String targetAccount, Integer money) {
23 // 查询原始账户
24 Account source = accountDao.findAccountByNum(sourceAccount);
25 // 查询目标账户
26 Account target = accountDao.findAccountByNum(targetAccount);
27 // 原始账号减钱
28 source.setMoney(source.getMoney() - money);
29 // 目标账号加钱
30 target.setMoney(target.getMoney() + money);
31 // 更新原始账号
32 accountDao.updateAccount(source);
33 //手动加入异常
34 int i = 1/0;
35 // 更新目标账号
36 accountDao.updateAccount(target);
37 System.out.println("转账完毕");
38 }
39 }

AccountTest.java(测试层)

 1 import org.junit.Test;
2 import org.junit.runner.RunWith;
3 import org.springframework.beans.factory.annotation.Autowired;
4 import org.springframework.test.context.ContextConfiguration;
5 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
6 import services.AccountService;
7
8 @RunWith(SpringJUnit4ClassRunner.class)
9 @ContextConfiguration(locations = "classpath:applicationContext.xml")
10 public class AccountTest {
11
12 @Autowired
13 private AccountService accountService;
14
15 @Test
16 public void testTransfer() {
17 String sourceAccount = "622200001";
18 String targetAccount = "622200002";
19 Integer money = 100;
20 accountService.transfer(sourceAccount,targetAccount,money);
21 }
22 }

执行结果:

控制台输出:转账完毕

查看MySQL数据库的运行结果:

1.刷新前:两个账户各有1000元

2.刷新后:1用户减少100,2用户增加100;执行成功

引入代理模式

TransactionManager.java(事务管理器)

 1 package transaction;
2
3 import org.aspectj.lang.annotation.*;
4 import org.springframework.beans.factory.annotation.Autowired;
5 import org.springframework.stereotype.Component;
6 import utils.ConnectionUtils;
7
8 import java.sql.SQLException;
9
10 @Component
11 @Aspect
12 public class TransactionManager {
13 // 数据库连接工具类
14 @Autowired
15 private ConnectionUtils connectionUtils;
16
17 @Pointcut("execution(* services.*.*(..))")
18 private void transactionPointcut() {
19 }
20
21 /**
22 * 开启事务
23 */
24 @Before("transactionPointcut()")
25 public void beginTransaction() {
26 try {
27 System.out.println("开启事务");
28 connectionUtils.getThreadConnection().setAutoCommit(false);
29 } catch (SQLException e) {
30 e.printStackTrace();
31 }
32 }
33
34 /**
35 * 提交事务
36 */
37 @AfterReturning("transactionPointcut()")
38 public void commit() {
39 try {
40 System.out.println("提交事务");
41 connectionUtils.getThreadConnection().commit();
42 } catch (SQLException e) {
43 e.printStackTrace();
44 }
45 }
46
47 /**
48 * 回滚事务
49 */
50 @AfterThrowing("transactionPointcut()")
51 public void rollback() {
52 try {
53 System.out.println("回滚事务");
54 connectionUtils.getThreadConnection().rollback();
55 } catch (SQLException e) {
56 e.printStackTrace();
57 }
58 }
59
60 /**
61 * 释放连接
62 */
63 @After("transactionPointcut()")
64 public void release() {
65 try {
66 System.out.println("释放连接");
67 connectionUtils.getThreadConnection().close();
68 } catch (SQLException e) {
69 e.printStackTrace();
70 }
71 connectionUtils.removeConnection();
72 }
73 }

TransactionProxyUtils.java(事务代理工具)

 1 package utils;
2
3 import org.springframework.beans.factory.annotation.Autowired;
4 import org.springframework.stereotype.Component;
5 import services.AccountService;
6 import transaction.TransactionManager;
7
8 import java.lang.reflect.InvocationHandler;
9 import java.lang.reflect.Method;
10 import java.lang.reflect.Proxy;
11
12 @Component
13 public class TransactionProxyUtils {
14 //被代理的业务类接口
15 @Autowired
16 private AccountService accountService;
17 //提供事务管理的工具类
18 @Autowired
19 private TransactionManager transactionManager;
20
21 /**
22 * 获取AccountService代理对象
23 *
24 * @return
25 */
26 public AccountService getAccountService() {
27 return (AccountService) Proxy.newProxyInstance(
28 accountService.getClass().getClassLoader(),
29 accountService.getClass().getInterfaces(),
30 new InvocationHandler() {
31 /**
32 * 添加事务的支持
33 *
34 * @param proxy 被代理的对象实例本身
35 * @param method 被代理对象正在执行的方法对象
36 * @param args 正在访问的方法参数对象
37 * @return
38 * @throws Throwable
39 */
40 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
41
42 //
43 Object rtValue = null;
44 try {
45 // 执行操作前开启事务
46 transactionManager.beginTransaction();
47 // 执行操作
48 rtValue = method.invoke(accountService, args);
49 // 执行操作后提交事务
50 transactionManager.commit();
51 // 返回结果
52 return rtValue;
53 } catch (Exception e) {
54 // 捕捉到异常执行回滚操作
55 transactionManager.rollback();
56 throw new RuntimeException(e);
57 } finally {
58 // 最终释放连接
59 transactionManager.release();
60 }
61 }
62 });
63
64 }
65 }

1.手动添加异常:int i = 1/0;

  输出结果

运行报错:

数据库丢失100错误:

2.数据库数值改为正常

再次运行后数据库的数值:

事务回滚:

SptingAOP的更多相关文章

随机推荐

  1. TypeORM Entity

    TypeORM Entity Entity Entity is a class that maps to a database table when using SQL database(or col ...

  2. web & js & touch & gesture

    web & js & touch & gesture 触摸 & 手势 https://caniuse.com/#feat=touch js https://develo ...

  3. eui & search select

    eui & search select https://element.eleme.io/#/zh-CN/component/select demo <template> < ...

  4. Objec.assign & bug

    Objec.assign & bug shallow copy https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Referenc ...

  5. Dart: path库

    path库pub地址 安装: dependencies: path: 使用: import 'dart:io'; import 'package:path/path.dart' as path; ma ...

  6. NGK全球巡回路演莫斯科站,共探BGV能否超越YFI?

    近日,NGK全球巡回路演在俄罗斯首都莫斯科落下帷幕.此次路演取得了空前的成功.路演伊始俄罗斯路演讲师Andrew致开幕辞,安德鲁称,俄罗斯作为未一个幅员辽阔的大国,区块链技术有着非常大的应用场景. 俄 ...

  7. Zookeeper从入门到删库跑路

    导语 ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,它包含一个简单的原语集,分布式应用程序可以基于它实现同步服务,配置维护和命名服务等.Zookeeper是hadoop的一个子项 ...

  8. 6. vue组件详解(一)

    主要内容: 1. 组件的基本使用 2. 全局组件和局部组件 3. 父组件和子组件 4. 组件语法糖的写法 5. 组件data关联的写法 6. 父子组件的通信 组件系统是 Vue 的一个重要概念,因为它 ...

  9. linux系统导出随笔

    导出时,不要用root用户忽略某张表的命令(多张表则直接往后加即可) --ignore-table=firewall_8088.operate_history --ignore-table=firew ...

  10. Docker daemon socket权限不足

    一.概述 普通用户执行命令:docker ps报错,具体信息如下: docker: Got permission denied while trying to connect to the Docke ...