用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. How to get the real screen size(screen resolution) by using js

    How to get the real screen size(screen resolution) by using js 获取用户屏幕的真实像素分辨率, 屏幕实际尺寸 window.deviceP ...

  2. leetcode & Mock Interview

    leetcode & Mock Interview https://leetcode.com/interview/ xgqfrms 2012-2020 www.cnblogs.com 发布文章 ...

  3. Vue 3.x Composition API

    Vue 3.x Composition API setup 调用时机 创建组件实例,然后初始化 props ,紧接着就调用setup 函数; 从生命周期钩子的视角来看,它会在 beforeCreate ...

  4. node.js & fs & file read & file write

    node.js & fs & file read & file write https://nodejs.org/api/fs.html const fs = require( ...

  5. 教你玩转CSS Overflow

    CSS 布局 - Overflow CSS overflow 属性用于控制内容溢出元素框时显示的方式. <style> #overflowTest { background: #4CAF5 ...

  6. web项目中各工具的作用

    一.HTML:用于搭建基础网页,展示网页的内容 Hyper Text Markup Language 超文本标记语言 ,是最基础的网页开发语言. * 超文本: * 超文本是用超链接的方法,将各种不同空 ...

  7. 一文读懂Servlet

    1 Servlet简介 Servlet就是sun公司开发动态web的一门技术 Sun在这些API中提供一个接口叫做:Servlet. 开发一个Servlet程序,只需两步: 编写一个类,实现Servl ...

  8. WPF MVVM实例三

    在没给大家讲解wpf mwm示例之前先给大家简单说下MVVM理论知识: WPF技术的主要特点是数据驱动UI,所以在使用WPF技术开发的过程中是以数据为核心的,WPF提供了数据绑定机制,当数据发生变化时 ...

  9. vscode好用插件总结

    做个记录:https://blog.csdn.net/xishining/article/details/90819481 1.Auto Rename Tag --自动重命名成对的HTML标记.假如你 ...

  10. 谈一下HashMap的底层原理是什么?

    底层原理:Map + 无序 + 键唯一 + 哈希表 (数组+Entry)+ 存取值 1.HashMap是Map接口的实现类.实现HashMap对数据的操作,允许有一个null键,多个null值. Co ...