SpringAOP需要解决的问题

SpringAOP需要解决的问题

  • 1. 传统编码的事务问题
  • 业务流程
  • 新的问题,当我们把事务交给service后,出现的问题。

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();
        }

    }

业务流程

  1. service层
    调用transactionManager.beginTransaction();
    时beginTransaction方法调用
    connectionUtils.getThreadConnection().setAutoCommit(false);
    创建一个连接,将连接放入线程中,并将事务设置为手动提交
  2. dao层
    service在调用dao层的时候,dao层执行sql语句的时候也会在线程中获取连接Connection,此时获取的时service层绑定的连接。
    connectionUtils.getThreadConnection();
    这个方法的作用就是,第一次调用时看看线程中有没有Connection,没有的话就从连接池中获取一个连接,然后绑定到线程中
  3. 如果程序发生异常的话在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();
    }

你可能感兴趣的:(SpringAOP需要解决的问题)