SpringAOP需要解决的问题
1. 传统编码的事务问题
sql的事务,存在于Connection(连接中),默认情况下都是自动提交,我们每一次执行一条sql语句都会获取一个Connection连接,而在正常情况下,我们的业务可能会操作多条sql语句,这些sql语句要么同时生效,要么同时回滚。
也就是说事务的控制不能在dao层中,必须要在service中进行。
如何保证多条sql同时生效,或同时回滚?
保证执行多条sql语句的连接Connection,只有一个
怎么实现:
将Connection与线程绑定,需要连接的时候在线程中拿
/**
* 获取连接的工具类
* 从线程上获取一个连接
* 如果线程中没有连接,就从数据源中拿,并且绑定到线程中
*/
@Component
public class ConnectionUtils {
ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
@Autowired
private DataSource dataSource;
/**
* 从线程中获取连接的方法,
* 也是将连接与线程绑定
* @return
*/
public Connection getThreadConnection(){
try{
Connection connection = tl.get();
if(connection == null){
//创建一个连接放在线程中
connection = dataSource.getConnection();
tl.set(connection);
}
return connection;
}catch(Exception e){
throw new RuntimeException(e);
}
}
public void removeConnection(){
tl.remove();
}
}
dao中执行sql语句必须使用本线程中绑定的连接(Connection)
runner.query(connectionUtils.getThreadConnection(), "select * from user", new BeanListHandler<User>(User.class));
service中事务的控制
public void zz(String userName1, String userName2, float addmoney) {
try{
transactionManager.beginTransaction();
User user1 = dao.findUserByName(userName1);
User user2 = dao.findUserByName(userName2);
user1.setMoney(user1.getMoney()-addmoney);
dao.updateUser(user1);
user2.setMoney(user2.getMoney()+addmoney);
// int i = 1/0;
dao.updateUser(user2);
transactionManager.commit();
}catch(Exception e){
transactionManager.rollback();
throw new RuntimeException(e);
}finally {
transactionManager.close();
}
}
具体代码流程,
需要两个工具类ConnectionUtils(负责从线程中获取绑定的连接,如果没有就创建一个连接放入线程中),TransactionManager(线程中的Connection的事务进行封装)
/**
* 获取连接的工具类
* 从线程上获取一个连接
* 如果线程中没有连接,就从数据源中拿,并且绑定到线程中
*/
@Component
public class ConnectionUtils {
ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
@Autowired
private DataSource dataSource;
/**
* 从线程中获取连接的方法,
* 也是将连接与线程绑定
* @return
*/
public Connection getThreadConnection(){
try{
Connection connection = tl.get();
if(connection == null){
connection = dataSource.getConnection();
tl.set(connection);
}
return connection;
}catch(Exception e){
throw new RuntimeException(e);
}
}
public void removeConnection(){
tl.remove();
}
}
@Component
public class TransactionManager {
@Autowired
private ConnectionUtils connectionUtils;
public void beginTransaction(){
try{
connectionUtils.getThreadConnection().setAutoCommit(false);
}catch(Exception e){
throw new RuntimeException(e);
}
}
public void commit(){
try {
connectionUtils.getThreadConnection().commit();
} catch (SQLException e) {
e.printStackTrace();
}
}
public void rollback(){
try {
connectionUtils.getThreadConnection().rollback();
} catch (SQLException e) {
e.printStackTrace();
}
}
public void close(){
try {
connectionUtils.getThreadConnection().close();
} catch (SQLException e) {
e.printStackTrace();
}
}
业务流程
- service层
调用transactionManager.beginTransaction();
时beginTransaction方法调用
connectionUtils.getThreadConnection().setAutoCommit(false);
创建一个连接,将连接放入线程中,并将事务设置为手动提交 - dao层
service在调用dao层的时候,dao层执行sql语句的时候也会在线程中获取连接Connection,此时获取的时service层绑定的连接。
connectionUtils.getThreadConnection();
这个方法的作用就是,第一次调用时看看线程中有没有Connection,没有的话就从连接池中获取一个连接,然后绑定到线程中 - 如果程序发生异常的话在service层中就应该回滚事务,
新的问题,当我们把事务交给service后,出现的问题。
几乎每一个方法都有重复代码
public List<User> findAll() {
System.out.println("----------------UserServiceImpl-----------------");
List<User> all;
try{
transactionManager.beginTransaction();//1.
all = dao.findAll();
transactionManager.commit();//2.
return all;
}catch(Exception e){
transactionManager.rollback();//3.
throw new RuntimeException(e);
}finally {
transactionManager.close();//4.
}
}
将代码抽取,使用动态代理技术对方法进行增强。
使用代理对象去创建service
@Component("beanFactory")
public class BeanFactory {
@Autowired
private IUserService userService;
@Autowired
private TransactionManager transactionManager;
public IUserService getUserSerivce(){
return (IUserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object o;
try{
transactionManager.beginTransaction();
o = method.invoke(userService, args);
transactionManager.commit();
return o;
}catch (Exception e){
transactionManager.rollback();
throw new RuntimeException(e);
}finally {
transactionManager.close();
}
}
});
}
}
代理后我们的service又回到了最开始的状态
public List<User> findAll() {
System.out.println("----------------UserServiceImpl-----------------");
return dao.findAll();
}