用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. 如何在 GitHUb 上使用 gitbook 发布一本在线书籍

    如何在 GitHUb 上使用 gitbook 发布一本在线书籍 ebook / pdf refs https://docs.gitbook.com/integrations/github xgqfrm ...

  2. holy shit CSDN

    holy shit CSDN 垃圾 CSDN 到处都是垃圾文章, 无人子弟 到处都是垃圾广告,看的恶心 毫无底线,窃取别人的知识成果,毫无版权意识 垃圾爬虫,垃圾小号 ...等等 Google Sea ...

  3. taro 三端开发

    taro 三端开发 wx 小程序, alipay 小程序,H5 https://taro-docs.jd.com/taro/docs/GETTING-STARTED.html#h5 https://t ...

  4. redux & multi dispatch & async await

    redux & multi dispatch & async await 同时发送多个 action, 怎么保证按序返回数据 dispatch multi actions http:/ ...

  5. c++ x86_x64挂钩无参数函数

    https://github.com/januwA/GameCheat #include "pch.h" #include <iostream> #include &l ...

  6. 「NGK每日快讯」2021.1.22日NGK公链第80期官方快讯!

  7. 【目标检测】用Fast R-CNN训练自己的数据集超详细全过程

    目录: 一.环境准备 二.训练步骤 三.测试过程 四.计算mAP 寒假在家下载了Fast R-CNN的源码进行学习,于是使用自己的数据集对这个算法进行实验,下面介绍训练的全过程. 一.环境准备 我这里 ...

  8. redis和mysql结合数据一致性方案

    缓存读: 缓存由于高并发高性能,已经被广泛的应用.在读取缓存方面做法一致.流程如下: 写缓存: 1.先更新数据库,再更新缓存 2.先更新数据库,再删除缓存. (1).先更新数据库,再更新缓存 这套方案 ...

  9. [转]RoboWare Studio的使用和发布器/订阅器的编写与测试

    原文地址:https://blog.csdn.net/han_l/article/details/77772352,转载主要方便随时查阅,如有版权要求,请及时联系. 开始ROS学习之前,先按照官网教程 ...

  10. SSL (Secure Sockets Layer)

    本文转载自SSL (Secure Sockets Layer) TLS简介 The Transport Layer Security (TLS) protocol aims primarily to ...