SptingAOP

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.数据库数值改为正常

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

事务回滚:

 

posted @ 2021-03-29 14:06  轩er  阅读(99)  评论(0)    收藏  举报