1、搭建dao层
package com.proxy.dao;
import com.proxy.entity.Account;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import java.util.List;
public class AccountDaoImpl implements AccountDao {
private QueryRunner queryRunner;
public Account findAccountByName(String accountName) {
try{
List<Account> query = queryRunner.query(" select id,account_name,money from ac where account_name = ?", new BeanListHandler<Account>(Account.class), accountName);
if(query == null || query.size() == 0){
return null;
}
if(query.size() >= 2){
throw new RuntimeException("数据大约2条");
}
Account account = query.get(0);
account.setAccountName(accountName);
return account;
}catch (Exception e){
throw new RuntimeException(e);
}
}
public void updateAccount(Account account) {
try{
queryRunner.update("update ac set money = ? where account_name = ?", account.getMoney(), account.getAccountName());
}catch (Exception e){
throw new RuntimeException(e);
}
}
public void setQueryRunner(QueryRunner queryRunner) {
this.queryRunner = queryRunner;
}
}
2、搭建service层
package com.proxy.service.impl;
import com.proxy.dao.AccountDao;
import com.proxy.entity.Account;
import com.proxy.service.AccountService;
/**
* 问题:每个方法都写txManagement 事务方法
* 代码比较臃肿,不易维护
*/
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public Account findAccountByName(String accountName) {
Account accountByName = accountDao.findAccountByName(accountName);
return accountByName;
}
public void updateAccount(Account account) {
accountDao.updateAccount(account);
}
public void transfer(String sourceName, String targetName, Float money) {
//根据A名称查询出账户
Account source = accountDao.findAccountByName(sourceName);
//根据B名称查询出账户
Account target = accountDao.findAccountByName(targetName);
//转出账户减钱
source.setMoney(source.getMoney() - money);
//转入账户加钱
target.setMoney(target.getMoney() + money);
//更新转出账户
accountDao.updateAccount(source);
//更新转入账户
accountDao.updateAccount(target);
}
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
}
3、配置bean.xml
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--配置service层-->
<bean id="accountService" class="com.proxy.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
<!--配置dao层-->
<bean id="accountDao" class="com.proxy.dao.AccountDaoImpl">
<property name="queryRunner" ref="queryRunner"></property>
</bean>
<!--配置QueryRunner-->
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
<constructor-arg name="ds" ref="dataSource"></constructor-arg>
</bean>
<!--配置数据源-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://120.25.236.19:3306/practice?useSSL=false"></property>
<property name="user" value="root"></property>
<property name="password" value="123456"></property>
</bean>
</beans>
4、测试
import com.proxy.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:bean.xml") //classes = BeanConfiguration.class
public class TestMain {
@Autowired
private AccountService accountService;
@Test
public void test(){
System.out.println(accountService.findAccountByName("aaa").getId());
}
}
改造成代理模式,对业务层方法进行增强
5、创建ConnectionUtils
package com.spring.utils;
import javax.sql.DataSource;
import java.sql.Connection;
/**
* 链接的工具类,它用于从数据源中获取一个链接,并且实现和线程的绑定
*/
public class ConnectionUtils {
private ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
/**
* 获取当前线程上的链接
*/
public Connection getThreadConnection(){
//1、先从ThreadLocal上进行获取
Connection connection = tl.get();
//2、判断当前线程上是否有链接
try{
if(connection == null){
//3、从数据源中获取一个链接,并存入ThreadLocal中
connection = dataSource.getConnection();
tl.set(connection);
}
}catch (Exception e){
throw new RuntimeException(e);
}
return connection;
}
public void removeConnection(){
tl.remove();
}
}
6、创建TransactionManagement
package com.spring.utils;
/**
* 和事务相关的工具类,它3包含了,开启事务,提交事务、回滚事务、释放事务
*/
public class TransactionManagement {
private ConnectionUtils connectionUtils;
public void setConnectionUtils(ConnectionUtils connectionUtils) {
this.connectionUtils = connectionUtils;
}
/**
* 开启事务
*/
public void beginTransaction(){
try{
connectionUtils.getThreadConnection().setAutoCommit(false);
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 提交事务
*/
public void commit(){
try{
connectionUtils.getThreadConnection().commit();
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 回滚事务
*/
public void rollback(){
try{
connectionUtils.getThreadConnection().rollback();
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 释放连接
*/
public void release(){
try{
connectionUtils.getThreadConnection().close();
connectionUtils.removeConnection();
}catch (Exception e){
e.printStackTrace();
}
}
}
7、改造dao
package com.spring.dao;
import com.spring.entity.Account;
import com.spring.utils.ConnectionUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import java.util.List;
public class AccountDaoImpl implements AccountDao {
private QueryRunner queryRunner;
private ConnectionUtils connectionUtils;
public Account findAccountByName(String accountName) {
try{
List<Account> query = queryRunner.query(connectionUtils.getThreadConnection()," select id,account_name,money from ac where account_name = ?", new BeanListHandler<Account>(Account.class), accountName);
if(query == null || query.size() == 0){
return null;
}
if(query.size() >= 2){
throw new RuntimeException("数据大约2条");
}
Account account = query.get(0);
account.setAccountName(accountName);
return account;
}catch (Exception e){
throw new RuntimeException(e);
}
}
public void updateAccount(Account account) {
try{
queryRunner.update(connectionUtils.getThreadConnection(),"update ac set money = ? where account_name = ?", account.getMoney(), account.getAccountName());
}catch (Exception e){
throw new RuntimeException(e);
}
}
public QueryRunner getQueryRunner() {
return queryRunner;
}
public void setQueryRunner(QueryRunner queryRunner) {
this.queryRunner = queryRunner;
}
public ConnectionUtils getConnectionUtils() {
return connectionUtils;
}
public void setConnectionUtils(ConnectionUtils connectionUtils) {
this.connectionUtils = connectionUtils;
}
}
8、创建代理对象的工厂
package com.spring.proxy;
import com.spring.service.AccountService;
import com.spring.utils.TransactionManagement;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
*
* 用于创建代理对象的工厂
*/
public class ProxyBeanFactory {
private AccountService accountService;
private TransactionManagement txManagement;
/**
* 生成accountService的代理对象
*/
public AccountService getAccountService(){
return (AccountService)Proxy.newProxyInstance(
accountService.getClass().getClassLoader(),
accountService.getClass().getInterfaces(),
new InvocationHandler() {
/**
* 添加事务的支持
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object returnValue = null;
try {
txManagement.beginTransaction();
returnValue = method.invoke(accountService, args);
txManagement.commit();
return returnValue;
} catch (Exception e) {
txManagement.rollback();
throw new RuntimeException(e);
} finally {
txManagement.release();
}
}
});
}
public void setAccountService(AccountService accountService) {
this.accountService = accountService;
}
public TransactionManagement getTxManagement() {
return txManagement;
}
public void setTxManagement(TransactionManagement txManagement) {
this.txManagement = txManagement;
}
}
9、改造bean.xml
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--配置service层-->
<bean id="accountService" class="com.spring.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
<!--配置dao层-->
<bean id="accountDao" class="com.spring.dao.AccountDaoImpl">
<property name="queryRunner" ref="queryRunner"></property>
<property name="connectionUtils" ref="connectionUtils"></property>
</bean>
<!--配置代理的service-->
<!--proxyAccountService 代理了 accountService 所以在control层调用,就调用代理-->
<bean id="proxyAccountService" factory-bean="proxyBeanFactory" factory-method="getAccountService"></bean>
<!--配置ProxyBeanFactory-->
<bean id="proxyBeanFactory" class="com.spring.proxy.ProxyBeanFactory">
<property name="txManagement" ref="transactionManagement"></property>
<property name="accountService" ref="accountService"></property>
</bean>
<!--配置QueryRunner-->
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype"></bean>
<!--配置数据源-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://120.25.236.19:3306/practice?useSSL=false"></property>
<property name="user" value="root"></property>
<property name="password" value="123456"></property>
</bean>
<!--配置Connection的工具类-->
<bean id="connectionUtils" class="com.spring.utils.ConnectionUtils">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置事务管理器-->
<bean id="transactionManagement" class="com.spring.utils.TransactionManagement">
<property name="connectionUtils" ref="connectionUtils"></property>
</bean>
</beans>
10、测试
在service设置报错,会正常回滚代码,
package com;
import com.spring.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:bean.xml") //classes = BeanConfiguration.class
public class TestMain {
@Autowired
@Qualifier("proxyAccountService")
private AccountService accountService;
@Test
public void test(){
accountService.transfer("aaa","bbb", 100f);
}
}
11、总结
本次应用的代理是接口代理。将事务特性:创建、提交、回滚、释放做了代理,也是将事务直接切入到方法中,从而体现出了切面的特性,也就是AOP。
git地址:https://gitee.com/Xiaokeworksveryhard/dynamic_proxy.git