整理思路
之前我们添加了一个转账的业务功能,同时也发现了事务不统一的问题。我们通过添加事务管理工具类的方法弥补了这个问题,但是同时又出现了新的问题。
最主要的问题就是,业务类对事务管理工具类的依赖(耦合)。现在我们就使用上面学到的动态管理来处理这个问题。
将代码恢复到事务合并之前。
package com.tianqicode.service.Impl;
import com.tianqicode.dao.AccountDao;
import com.tianqicode.domain.Account;
import com.tianqicode.service.AccountService;
import java.util.List;
public class AccountServiceImpl implements AccountService {
//这里调用Dao持久层的对象
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public List<Account> findAllAccount() {
return accountDao.findAllAccount();
}
public Account findAccountById(Integer accountId) {
return accountDao.findAccountById(accountId);
}
public void save(Account account) {
accountDao.save(account);
System.out.println("保存成功");
}
public void update(Account account) {
accountDao.update(account);
System.out.println("更新成功。。。");
}
public void delete(Integer accountId) {
accountDao.delete(accountId);
System.out.println("删除成功。。。");
}
public void transfer(String sourceName, String targetName, Float money) {
Account sourceAccount = accountDao.findAccountByName(sourceName);
Account targetAccount = accountDao.findAccountByName(targetName);
sourceAccount.setMoney(sourceAccount.getMoney() - money);
targetAccount.setMoney(targetAccount.getMoney() + money);
accountDao.update(sourceAccount);
int a = 1/0;
accountDao.update(targetAccount);
System.out.println(sourceName + "向" + targetName +"转账" + money + "元成功");
}
}
简单来说,我们需要对这个类在不改动业务代码的同时进行加强,将每一个方法中的独立的事务合并为一个。不改动代码也就意味着不对这个业务类添加任何的方法依赖。也就达成了我们解耦合的目的。
现在就需要借助代理来对代码进行增强。也就需要将合并事务的操作放在调用处理器中,再在调用处理器中通过反射动态调用业务类中的方法,,并且再其中添加方法的增强。
思路理清楚后,开始code。
package com.tianqicode.factory;
import com.tianqicode.domain.Account;
import com.tianqicode.service.AccountService;
import com.tianqicode.utils.TransactionManager;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 用于创建Service的代理对象的工厂
*/
public class BeanFactory {
private AccountService accountService;
private TransactionManager transactionManager;
/**
* 在配置文件中借助set方法注入对实现类AccountServiceImpl依赖
* @param transactionManager
*/
public void setTransactionManager(TransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
public final void setAccountService(AccountService accountService) {
this.accountService = accountService;
}
/**
* 获取Service的代理对象
* @return
*/
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 {
//1、开启事务
transactionManager.beginTransaction();
//2、执行操作,动态调用AccountServiceImpl对象的方法
returnValue = method.invoke(accountService, args);
//3、提交事务
transactionManager.commitTransaction();
//4、返回结果
return returnValue;
}catch (Exception e){
//5、回滚操作
transactionManager.rollbackTransaction();
throw new RuntimeException(e);
}finally {
//6、释放连接
transactionManager.release();
}
}
});
}
}
这样就写好了一个业务类的代理类,在配置文件中添加或修改相关的配置
<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对象 -->
<bean id="beanFactory" class="com.tianqicode.factory.BeanFactory">
<property name="transactionManager" ref="transactionManager"></property>
<property name="accountService" ref="accountService"></property>
</bean>
<!-- 准备service类bean对象 -->
<bean id="accountService" class="com.tianqicode.service.Impl.AccountServiceImpl">
<!-- 在service类中注入dao依赖 -->
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 准备dao类bean对象 -->
<bean id="accountDao" class="com.tianqicode.dao.Impl.AccountDaoImpl">
<!-- 在dao类中注入QueryRunner依赖 -->
<property name="runner" ref="runner"></property>
<!-- 注入连接工具类依赖 -->
<property name="connectionUtil" ref="connection"></property>
</bean>
<!-- 准备QueryRunner类bean对象 -->
<bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype"></bean>
<!-- 准备数据源bean对象 -->
<bean id="ds" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- 在数据源中准备连接数据库的必要信息 -->
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/javaStudy"></property>
<property name="user" value="root"></property>
<property name="password" value="admin"></property>
</bean>
<!-- 准备连接工具类的bean对象 -->
<bean id="connection" class="com.tianqicode.utils.ConnectionUtil">
<!-- 注入数据源依赖 -->
<property name="source" ref="ds"></property>
</bean>
<!-- 准备事务管理工具类bean对象 -->
<bean id="transactionManager" class="com.tianqicode.utils.TransactionManager">
<!-- 注入连接工具类对象 -->
<property name="connectionUtil" ref="connection"></property>
</bean>
</beans>
在测试类中测试一下功能
package com.tianqicode.test;
//import com.tianqicode.config.SpringConfiguration;
import com.tianqicode.domain.Account;
import com.tianqicode.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.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.List;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:bean.xml")
public class AccountServiceTest {
@Autowired
//由于代理类也同时实现了AccountService接口,所以需要借助Qualifier注解来准确定位需要注入的依赖
@Qualifier("proxyAccountService")
private AccountService as;
@Test
public void testTransfer() {
as.transfer("TQ","YY",100f);
}
}
测试业务类功能以及数据的一致性都完整的实现了之前使用事务管理工具类所完成的任务,并且代码更加整洁清新,同时还解决了业务类对事务管理类的依赖。