1、对象之间耦合的问题
假设代码如下实现,service内部调dao层实现。
public class TransferServiceImpl implements TransferService {
private AccountDao accountDao = new JdbcAccountDaoImpl();
}
在Service层调上面语句,显然存在对象耦合问题,不符合面向接口编程的规则。假设Dao层接口有了新的实现方式,必须修改service层代码。
如果不用new对象,则需要用反射创建Class.forName
。反射需要全限定类名,如果写在上面的代码中,也是耦合。因此要将全限定类名,拿到xml中,或者通过注解来获取。
<bean id="accountDao" class="com.lzp.edu.dao.impl.JdbcTemplateDaoImpl"></bean>
<bean id="transferService" class="com.lzp.edu.service.impl.TransferServiceImpl">
<property name="AccountDao" ref="accountDao"></property>
</bean>
使用工厂方法设计模式来生成对象。通过全限定类名生成对象,存在BeanFactory工厂对象的Map集合中,以此保证单例。此时需要下面几个操作:
- 解析xml
- 创建出所有的bean对象,存到map中(反射)
- 对有property标签的对象,做属性注入(也是反射)。
做完上面的工作,也就实现基本实现了IOC。
public class TransferServiceImpl implements TransferService {
//private AccountDao accountDao = new JdbcAccountDaoImpl();
// private AccountDao accountDao = (AccountDao) BeanFactory.getBean("accountDao");
// 最佳状态
private AccountDao accountDao;
}
2、切面逻辑的实现–添加事务控制
假设service层方法有这样两句代码
public void transfer(String fromCardNo, String toCardNo, int money) {
accountDao.updateAccountByCardNo(to); // A
int c = 1/0; // 数学异常
accountDao.updateAccountByCardNo(from); // B
}
如果不加额外的事务控制,必然导致A执行了更新了数据,而B没有执行到,前后数据不一致。原因是A和B两个操作,分别去获取JDBC连接,分别执行数据库操作,然后各自提交了。
通过上面分析,解决事务问题只要当前线程获取同一个连接,关闭自动提交,再所有Service方法所有操作完成后提交事务即可。
对于线程管理连接,使用ThreadLocal
作为容器。
private ThreadLocal<Connection> threadLocal = new ThreadLocal<>(); // 存储当前线程的连接
此时就可以在A前面和B后面添加事务控制语句,解决事务控制问题。
public void transfer(String fromCardNo, String toCardNo, int money) throws Exception {
connectionUtils.getCurrentThreadConn().setAutoCommit(false); // 开启事务(关闭事务的自动提交)
accountDao.updateAccountByCardNo(to);
int c = 1/0;
accountDao.updateAccountByCardNo(from);
connectionUtils.getCurrentThreadConn().commit();
}
代码中,开启事务、提交事务的代码就是横向逻辑代码,与原来的转账业务没有关系。事务控制用动态代理实现,或是JDK动态代理,或者Cglib等。
给Service类创建代理对象,调用方法时增强。
public Object getJdkProxy(Object obj) {
// 获取代理对象
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
try{
transactionManager.beginTransaction(); // 开启事务(关闭事务的自动提交)
result = method.invoke(obj,args);
transactionManager.commit(); // 提交事务
}catch (Exception e) {
transactionManager.rollback(); // 回滚事务
throw e; // 抛出异常便于上层servlet捕获
}
return result;
}
});
}
此时,动态代理添加事务功能也就实现了。期间产生的几个类,也可以通过IOC的方式管理起来。
代码地址:https://gitee.com/dtyytop/devdemos/tree/master/spring/banktransfer/lagou-transfer