原业务层代码:
AccountServiceImpl_OLD.java
public class AccountServiceImpl_OLD implements AccountService {
private AccountDao accountDao;
private TransactionManager transactionManager;
// 实现set方法,使用Spring依赖注入
public void setTransactionManager(TransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
// 实现set方法,以便Spring使用set方法完成注入
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public List<Account> findAll() {
try {
// 1、开启事务
transactionManager.beginTransaction();
// 2、执行操作
List<Account> accounts = accountDao.findAll();
// 3、提交事务
transactionManager.commit();
// 4、返回结果
return accounts;
} catch (Exception e) {
// 5、回滚操作
transactionManager.rollback();
throw new RuntimeException(e);// 如果产生异常,程序不在执行。
} finally {
// 6、释放连接
transactionManager.release();
}
}
@Override
public Account findAccountById(Integer id) {
try {
// 1、开启事务
transactionManager.beginTransaction();
// 2、执行操作
Account account = accountDao.findAccountById(id);
// 3、提交事务
transactionManager.commit();
// 4、返回结果
return account;
} catch (Exception e) {
// 5、回滚操作
transactionManager.rollback();
throw new RuntimeException(e);// 如果产生异常,程序不在执行。
} finally {
// 6、释放连接
transactionManager.release();
}
}
@Override
public void saveAccount(Account account) {
try {
// 1、开启事务
transactionManager.beginTransaction();
// 2、执行操作
accountDao.saveAccount(account);
// 3、提交事务
transactionManager.commit();
} catch (Exception e) {
// 5、回滚操作
transactionManager.rollback();
} finally {
// 6、释放连接
transactionManager.release();
}
}
@Override
public void updateAccount(Account account) {
try {
// 1、开启事务
transactionManager.beginTransaction();
// 2、执行操作
accountDao.updateAccount(account);
// 3、提交事务
transactionManager.commit();
} catch (Exception e) {
// 5、回滚操作
transactionManager.rollback();
} finally {
// 6、释放连接
transactionManager.release();
}
}
@Override
public void deleteAccount(Integer id) {
try {
// 1、开启事务
transactionManager.beginTransaction();
// 2、执行操作
deleteAccount(id);
// 3、提交事务
transactionManager.commit();
} catch (Exception e) {
// 5、回滚操作
transactionManager.rollback();
} finally {
// 6、释放连接
transactionManager.release();
}
}
@Override
public void transfer(String sourceName, String targetName, Float money) {
try {
// 1、开启事务
transactionManager.beginTransaction();
// 2、执行操作
// 2.1.根据sourceName查询转出账户
Account sourceAccount = accountDao.findAccountByName(sourceName);
// 2.2.根据targetName查询转入账户
Account targetAccount = accountDao.findAccountByName(targetName);
// 2.3.转出账户扣钱
sourceAccount.setMoney(sourceAccount.getMoney() - money);
// 2.4.转入账户加钱
targetAccount.setMoney(targetAccount.getMoney() + money);
// 2.5.更新转出账户
accountDao.updateAccount(sourceAccount);
int i = 1/0;
// 2.6.更新转入账户
accountDao.updateAccount(targetAccount);
// 3、提交事务
transactionManager.commit();
} catch (Exception e) {
// 5、回滚操作
transactionManager.rollback();
e.printStackTrace();
} finally {
// 6、释放连接
transactionManager.release();
}
}
}
以上是原有业务层代码,每次调用执行sql语句之前都会开启事务,然后提交事务,若产生异常则回滚,最后将连接归还到连接池。
问题:
业务层方法变得臃肿了,里面充斥着很多重复代码。并且业务层方法和事务控制方法耦合了。
下面,我们通过动态代理(此处演示基于接口的动态代理)实现抽取公共方法,增强业务层方法。
第一步:抽取原业务层代码,创建BeanFactory.java
,用于创建Service的代理对象的工厂
public class BeanFactory {
private AccountService accountService;
private TransactionManager transactionManager;
// 实现set方法,使用Spring依赖注入
public void setTransactionManager(TransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
/**
* getAccountService方法的匿名内部类引用了accountService,此处应该用final修饰
* @param accountService
*/
public final void setAccountService(AccountService accountService) {
this.accountService = accountService;
}
/**
* 获取Service的代理对象,不能直接返回AccountService
* @return
*/
public AccountService getAccountService() {
AccountService proxyAccountService = (AccountService)Proxy.newProxyInstance(accountService.getClass().getClassLoader(), accountService.getClass().getInterfaces(), new InvocationHandler() {
/**
* 添加事务的支持
* @param proxy
* @param method
* @param args
* @return 返回值
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object rtValue = null;
try {
// 1、开启事务
transactionManager.beginTransaction();
// 2、执行操作
rtValue = method.invoke(accountService, args);
// 3、提交事务
transactionManager.commit();
// 4、返回结果
return rtValue;
} catch (Exception e) {
// 5、回滚操作
transactionManager.rollback();
throw new RuntimeException(e);// 如果产生异常,程序不在执行。
} finally {
// 6、释放连接
transactionManager.release();
}
}
});
return proxyAccountService;
}
}
第二步:抽取业务层代码之后
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
// 实现set方法,以便Spring使用set方法完成注入
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public List<Account> findAll() {
return accountDao.findAll();
}
@Override
public Account findAccountById(Integer id) {
return accountDao.findAccountById(id);
}
@Override
public void saveAccount(Account account) {
accountDao.saveAccount(account);
}
@Override
public void updateAccount(Account account) {
accountDao.updateAccount(account);
}
@Override
public void deleteAccount(Integer id) {
deleteAccount(id);
}
@Override
public void transfer(String sourceName, String targetName, Float money) {
// 2.1.根据sourceName查询转出账户
Account sourceAccount = accountDao.findAccountByName(sourceName);
// 2.2.根据targetName查询转入账户
Account targetAccount = accountDao.findAccountByName(targetName);
// 2.3.转出账户扣钱
sourceAccount.setMoney(sourceAccount.getMoney() - money);
// 2.4.转入账户加钱
targetAccount.setMoney(targetAccount.getMoney() + money);
// 2.5.更新转出账户
accountDao.updateAccount(sourceAccount);
// int i = 1/0;
// 2.6.更新转入账户
accountDao.updateAccount(targetAccount);
}
}
第三步:
配置 Spring 的 IOC 容器
beam.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--配置代理的service-->
<bean id="proxyAccountService" factory-bean="beanFactory" factory-method="getAccountService"></bean>
<!--配置beanFactory-->
<bean id="beanFactory" class="com.hxh.factory.BeanFactory">
<!--注入service-->
<property name="accountService" ref="accountService"></property>
<!--注入事务管理器-->
<property name="transactionManager" ref="transactionManager"></property>
</bean>
<!--配置Service-->
<bean id="accountService" class="com.hxh.service.impl.AccountServiceImpl">
<!--注入dao对象-->
<property name="accountDao" ref="accountDao"></property>
</bean>
<!--配置dao-->
<bean id="accountDao" class="com.hxh.dao.impl.AccountDaoImpl">
<!--注入QueryRunner-->
<property name="runner" ref="runner"></property>
<!--注入ConnectionUtils-->
<property name="connectionUtils" ref="connectionUtils"></property>
</bean>
<!--配置QueryRunner对象。QueryRunner没有提供set注入方式,只能使用构造函数注入
QueryRunner默认是单例的。为了避免多个dao使用同一个对象,线程互相干扰。使用scope属性是QueryRunner为多例对象,每次使用时都重新创建新对象
-->
<bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype"></bean>
<!--配置ConnectionUtils-->
<bean id="connectionUtils" class="com.hxh.utils.ConnectionUtils">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置事务管理器TransactionManager-->
<bean id="transactionManager" class="com.hxh.utils.TransactionManager">
<property name="connectionUtils" ref="connectionUtils"></property>
</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://localhost:3306/spring"></property>
<property name="user" value="root"></property>
<property name="password" value="88888888"></property>
</bean>
</beans>
第四步:
通过ApplicationContext获取的业务层bean为 proxyAccountService
测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:bean.xml")
public class AccountServiceTest {
@Autowired
@Qualifier("proxyAccountService")
private AccountService as;
@Test
public void testTransfer() {
as.transfer("A", "B", 100f);
}
}