代理模式
代理模式作為23種經典設計模式之一,其比較官方的定義為“為其他對象提供一種代理以控制對這個對象的通路”,簡單點說就是,之前A類自己做一件事,在使用代理之後,A類不直接去做,而是由A類的代理類B來去做。代理類其實是在之前類的基礎上做了一層封裝。java中有靜态代理、JDK動态代理、CGLib動态代理的方式。靜态代理指的是代理類是在編譯期就存在的,相反動态代理則是在程式運作期動态生成的
靜态代理 & 動态代理
1 靜态代理
一個接口,兩個實作類,代理實作類組合真實實作類
2 動态代理
JDK動态代理和CGlib位元組碼動态代理
簡單轉賬功能
建立Maven項目名為“spring-aop“
準備資料
# 删除spring_aop資料庫
drop database if exists spring_aop;
# 建立spring_aop資料庫
create database spring_aop;
# 使用spring_aop資料庫
use spring_aop;
# 建立account表
create table account (
id int(11) auto_increment primary key,
accountNum varchar(20) default NULL,
money int(8) default 0
);
# 新增資料
insert into account (accountNum, money) values
("622200001",1000),("622200002",1000);
導包
<!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.13.RELEASE</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-dbutils/commons-dbutils -->
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.7</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.23</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
核心配置檔案
<?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:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
https://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
">
<!-- bean definitions here -->
<context:component-scan base-package="dao"/>
<context:component-scan base-package="services"/>
<context:component-scan base-package="utils"/>
</beans>
代碼編寫
package utils;
@Component
public class ConnectionUtils {
private ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
@Autowired
private ComboPooledDataSource dataSource;
/**
* 獲得目前線程綁定的連接配接
*
* @return
*/
public Connection getThreadConnection() {
try {
// 看線程是否綁了連接配接
Connection conn = tl.get();
if (conn == null) {
// 從資料源擷取一個連接配接
conn = dataSource.getConnection();
// 和線程局部變量 綁定
tl.set(conn);
}
// 傳回線程連接配接
return tl.get();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
/**
* 把連接配接和目前線程進行解綁
*/
public void remove() {
tl.remove();
}
}
Account子產品實體類:Account.java
package entity;
public class Account {
private Integer id;
private String accountNum;
private Integer money;
// 省略getter&setter方法
}
Account子產品Dao層:AccountDao.java
package dao;
public interface AccountDao {
/**
* 更新
*
* @param account
*/
void updateAccount(Account account);
/**
* 根據編号查詢賬戶
*
* @param accountNum
* @return 如果沒有結果就傳回null,如果結果集超過一個就抛異常,如果有唯一的一個結果就傳回
*/
Account findAccountByNum(String accountNum);
}
Account子產品Dao層實作類:AccountDaoImpl.java
package dao.impl;
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {
// 資料庫查詢工具類
@Autowired
private QueryRunner runner;
// 資料庫連接配接工具類
@Autowired
private ConnectionUtils connectionUtils;
/**
* 更新
*
* @param account
*/
public void updateAccount(Account account) {
try {
runner.update(connectionUtils.getThreadConnection(),
"update account set accountNum=?,money=? where id=?",
account.getAccountNum(), account.getMoney(), account.getId());
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
/**
* 根據編号查詢賬戶
*
* @param accountNum
* @return 如果沒有結果就傳回null,如果結果集超過一個就抛異常,如果有唯一的一個結果就傳回
*/
public Account findAccountByNum(String accountNum) {
List<Account> accounts = null;
try {
accounts = runner.query(connectionUtils.getThreadConnection(),
"select * from account where accountNum = ? ",
new BeanListHandler<Account>(Account.class),
accountNum);
} catch (SQLException e) {
throw new RuntimeException(e);
}
if (accounts == null || accounts.size() == 0) {
// 如果沒有結果就傳回null
return null;
} else if (accounts.size() > 1) {
// 如果結果集超過一個就抛異常
throw new RuntimeException("結果集不唯一,資料有問題");
} else {
// 如果有唯一的一個結果就傳回
return accounts.get(0);
}
}
}
Account子產品Service層:AccountService.java
package services;
public interface AccountService {
/**
* 轉賬
*
* @param sourceAccount 轉出賬戶
* @param targetAccount 轉入賬戶
* @param money 轉賬金額
*/
void transfer(String sourceAccount, String targetAccount, Integer money);
}
Account子產品Service層實作類:AccountServiceImpl.java
package services.impl;
@Service("accountService")
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
/**
* 轉賬
*
* @param sourceAccount 轉出賬戶
* @param targetAccount 轉入賬戶
* @param money 轉賬金額
*/
public void transfer(String sourceAccount, String targetAccount, Integer money) {
// 查詢原始賬戶
Account source = accountDao.findAccountByNum(sourceAccount);
// 查詢目标賬戶
Account target = accountDao.findAccountByNum(targetAccount);
// 原始賬号減錢
source.setMoney(source.getMoney() - money);
// 目标賬号加錢
target.setMoney(target.getMoney() + money);
// 更新原始賬号
accountDao.updateAccount(source);
// 更新目标賬号
accountDao.updateAccount(target);
System.out.println("轉賬完畢");
}
}
Account子產品測試類:AccountTest.java
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class AccountTest {
@Autowired
private AccountService accountService;
@Test
public void testTransfer() {
accountService.transfer("622200001", "622200002", 100);
}
}
引入AOP(XML)
代碼實作
删除事務代理工具類:TransactionProxyUtils.java
導入aspectjweaver包
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.3</version>
</dependency>
配置檔案中添加 AOP 的相關配置
<!-- aop相關的節點配置 -->
<aop:config>
<!-- 切入點 表示哪些類的哪些方法在執行的時候會應用Spring配置的通知進行增強 -->
<aop:pointcut expression="execution ( * services.*.*(..))" id="pc"/>
<!-- 配置切面類的節點 作用主要就是整合通知和切入點 -->
<aop:aspect ref="transactionManager">
<aop:before method="beginTransaction" pointcut-ref="pc"/>
<aop:after-returning method="commit" pointcut-ref="pc"/>
<aop:after method="release" pointcut-ref="pc"/>
<aop:after-throwing method="rollback" pointcut-ref="pc"/>
</aop:aspect>
</aop:config>
修改測試類代碼
XML改注解(AOP)
代碼實作
删除XML中的AOPXML配置并注解代理模式
<!-- 注解 開啟代理模式 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
注釋事務管理器類:TransactionManager.java
package transaction;
@Component
@Aspect
public class TransactionManager {
// 資料庫連接配接工具類
@Autowired
private ConnectionUtils connectionUtils;
@Pointcut("execution(* services.*.*(..))")
private void transactionPointcut() {
}
/**
* 開啟事務
*/
@Before("transactionPointcut()")
public void beginTransaction() {
try {
System.out.println("開啟事務");
connectionUtils.getThreadConnection().setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 送出事務
*/
@AfterReturning("transactionPointcut()")
public void commit() {
try {
System.out.println("送出事務");
connectionUtils.getThreadConnection().commit();
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 復原事務
*/
@AfterThrowing("transactionPointcut()")
public void rollback() {
try {
System.out.println("復原事務");
connectionUtils.getThreadConnection().rollback();
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 釋放連接配接
*/
@After("transactionPointcut()")
public void release() {
try {
System.out.println("釋放連接配接");
connectionUtils.getThreadConnection().close();
} catch (SQLException e) {
e.printStackTrace();
}
connectionUtils.removeConnection();
}
}
執行結果