主流框架二:Spring(4)动态代理实现事务控制

一、事务控制

事务我们在java web中有涉及。Spring中 我们使用了 connection 对象的 setAutoCommit(true)此方式控制事务,如果我们每次都执行一条 sql 语句,没有问题,但是如果业务方法一次要执行多条 sql语句,这种方式就无法实现功能了。在这里插入图片描述

解决办法:
让业务层来控制事务的提交和回滚。(这个我们之前已经在 web 阶段讲过了)
改造后的业务层实现类:
用 注:此处没有使用 spring 的 的IOC.

/**
* 账户的业务层实现类
*/
public class AccountServiceImpl implements IAccountService {

	private IAccountDao accountDao = new AccountDaoImpl();
	
	@Override
	public List<Account> findAllAccount() {
		List<Account> accounts = null;
		try {
			TransactionManager.beginTransaction();
			accounts = accountDao.findAll();
			TransactionManager.commit();
			return accounts;
		} catch (Exception e) {
			TransactionManager.rollback();
			e.printStackTrace();
		}finally {
			TransactionManager.release();
		}
		return null;
	}
	......
}

下面来看一下这两个工具类 ConnectionUtils 和 TransactionManager

1.ConnectionUtils

连接的工具类,它用于从数据源中获取一个连接,并且实现和线程绑定,所以之后我们获取当前线程的连接便可以直接connectionUtils.getThreadConnection()

/**
 * 连接的工具类,它用于从数据源中获取一个连接,并且实现和线程绑定
 */
public class ConnectionUtils {

    private ThreadLocal<Connection> tl = new ThreadLocal<Connection>();

    private DataSource dataSource;

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    /**
     * 获取当前线程上的连接
     * @return
     */
    public Connection getThreadConnection() {

            try {
                //1.先从ThreadLocal上获取
                Connection connection = tl.get();
                //2.判断当前线程上是否有连接
                if (connection == null) {
                    //3.从数据源中获取一个连接,并且存入ThreadLocal中
                    connection = dataSource.getConnection();
                    tl.set(connection);
                }
                //4.返回当前线程上的连接
                return connection;
            }catch (Exception e) {
                throw new RuntimeException(e);
            }
    }

    /**
     * 把链接和线程解绑
     */
    public void removeConnection() {
        tl.remove();
    }
}

2.TransactionManager

和事务管理相关的工具类,它包含了:开启事务,提交事务,回滚事务和释放连接(总管理)

例如:connectionUtils.getThreadConnection().setAutoCommit(false); 就等价于connection.setAutoCommit(false);

**
 * 和事务管理相关的工具类,它包含了:开启事务,提交事务,回滚事务和释放连接
 * @author Mango
 */
public class TransactionManager {

    private ConnectionUtils connectionUtils;

    public void setConnectionUtils(ConnectionUtils connectionUtils) {
        this.connectionUtils = connectionUtils;
    }

    /**
     * 开启事务
     */
    public void beginTransaction() {
        try {
            connectionUtils.getThreadConnection().setAutoCommit(false);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 提交事务
     */
    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 release() {
        try {
            connectionUtils.getThreadConnection().close();//还回连接池中
            connectionUtils.removeConnection();//连接与线程解绑
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

3.新的问题

上一小节的代码,通过对业务层改造,已经可以实现事务控制了,但是由于我们添加了事务控制,也产生了一
个新的问题:

@Override
    public List<Account> findAllAccount() {
        try {
            //1.开启事务
            txManager.beginTransaction();
            //2.执行操作
            List<Account> accounts = accountDao.findAllAccount();
            //3.提交事务
            txManager.commit();
            //4.返回结果
            return accounts;
        }catch (Exception e){
            //5.回滚操作
            txManager.rollback();
            throw new RuntimeException(e);
        }finally {
            //6.释放连接
            txManager.release();
        }

    }

    @Override
    public Account findAccountById(Integer accountId) {
        try {
            //1.开启事务
            txManager.beginTransaction();
            //2.执行操作
            Account account = accountDao.findAccountById(accountId);
            //3.提交事务
            txManager.commit();
            //4.返回结果
            return account;
        }catch (Exception e){
            //5.回滚操作
            txManager.rollback();
            throw new RuntimeException(e);
        }finally {
            //6.释放连接
            txManager.release();
        }
    }

业务层方法变得臃肿了,里面充斥着很多重复代码。并且业务层方法和事务控制方法耦合了

试想一下,如果我们此时提交,回滚,释放资源中任何一个方法名变更,都需要修改业务层的代码,况且这还只是一个业务层实现类,而实际的项目中这种业务层实现类可能有十几个甚至几十个。

二、动态代理回顾

在这里插入图片描述
1、 动态代理的特点

(1)字节码随用随创建,随用随加载。
(2)它与静态代理的区别也在于此。因为静态代理是字节码一上来就创建好,并完成加载。
(3)装饰者模式就是静态代理的一种体现。

2.动态代理的作用

**不修改源码的基础上对方法增强**

3.动态代理的分类:

(1)基于接口的动态代理

(1)基于接口的动态代理:
涉及的类:Proxy
提供者:JDK官方

如何创建代理对象:
使用proxy类中的newProxyInstance方法(要求:被代理的类至少实现其一个接口(例如IProducer,而不能用Producer来实现),如果没有就不能使用)

	newProxyInstance方法
	参数:1.ClassLoader:类加载器
		它是用于加载 代理对象字节码的。被代理对象的类加载器(固定写法)。
		
		2Interfaces:字节码数组
		它是用于让代理对象和被代理对象有相同的方法,实现相同的接口。(固定写法)
		
		3.InvocationHandler:用于提供增强的代码
		它是让我们写如何代理。我们一般都是写一个该接口的实现类,通常情况下都是匿名内部类,但不是必须
		此接口的实现类都是谁用谁写。
/**
 * 模拟一个service
 */
public class Client {
    public static void main(String[] args) {
    
        final Producer producer = new Producer();//必须是最终的
        
                IProducer proxyProducer =(IProducer) Proxy.newProxyInstance(
                参数(1)producer.getClass().getClassLoader(),
                参数(2)producer.getClass().getInterfaces(),
                参数(3new InvocationHandler() {
                
                    /**
                     * 作用:执行被代理对象的任何接口的方法都会经过该方法
                     * 方法参数的含义:
                     * @param proxy  代理对象的引用
                     * @param method 当前执行的方法
                     * @param args   当前执行方法所需的参数
                     * @return       和被代理对象方法有相同的返回值
                     * @throws Throwable
                     */
                     
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //提供增强方法的代码
                        Object returnValue = null;
                        //1.获取方法执行的参数
                        Float money = (Float) args[0];
                        //2.判断当前方法是不是销售
                        if("saleProduct".equals(method.getName())) {
                            returnValue = method.invoke(producer, money * 0.8f);
                        }
                        return returnValue;//方法执行一遍便有相同返回值
                    }
                });

        proxyProducer.saleProduct(10000f);
    }
}

(2)基于子类的动态代理

基于子类的动态代理

提供者:第三方的 CGLib,如果报 asmxxxx 异常,需要导入 asm.jar。需要导入jar包
要求:被代理类不能用 final 修饰的类(最终类)。

使用 CGLib 的 的 Enhancer 类创建代理对象

(2)基于子类的动态代理:
涉及的类:Enhancer
提供者:第三方cglib库

     如何创建代理对象:
        使用Enhancer中的create方法(要求:被代理的类不能是最终类)

     create中的参数:
        1.class:字节码
            用于指定被代理对象的字节码(这里Producer被代理)
        2.callback:用于提供增强的代码
            一般写的都是该接口的子接口实现类:MenthInterceptor
/**
 * 模拟一个消费者
 */
public class Client {
    public static void main(String[] args) {
    
        final Producer producer = new Producer();

        Producer cglibProducer =(Producer) Enhancer.create(producer.getClass(), new MethodInterceptor() {
            /**
             *
             * @param o
             * @param method
             * @param objects
                以上三个参数和基于接口的动态代理中的invoke方法的参数时一样的

             * @param methodProxy 当前执行方法的代理对象
             * @return
             * @throws Throwable
             */
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                //提供增强方法的代码
                Object returnValue = null;
                //1.获取方法执行的参数
                Float money = (Float) objects[0];
                //2.判断当前方法是不是销售
                if("saleProduct".equals(method.getName())) {
                    returnValue = method.invoke(producer, money * 0.8f);
                }
                return returnValue;//方法执行一遍便有相同返回值
            }
        });
       cglibProducer.saleProduct(10000f);
}
}

三、动态代理实现事务控制

1.用于创建service代理对象的factory

解决案例中的问题:
创建客户业务层对象工厂(当然也可以创建其他业务层对象,只不过我们此处不做那么繁琐)

/**
 * 用于创建service代理对象的factory
 * @author Mango
 */
public class BeanFactory {

    private IAccountService accountService;

    public final void setAccountService(IAccountService accountService) {
        this.accountService = accountService;
    }

    private TransactionManager transactionManager;

    public void setTransactionManager(TransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    }

    public IAccountService getAccountService() {
        return (IAccountService) 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 returnValue = null;
                        try {
                            //1.开启事务
                            transactionManager.beginTransaction();
                            //2.执行操作
                            returnValue = method.invoke(accountService, args);
                            //3.提交事务
                            transactionManager.commit();
                            //4.返回结果
                            return returnValue;
                        } catch (Exception e) {
                            //5.回滚
                            transactionManager.rollback();
                            throw new RuntimeException(e);
                        } finally {
                            //6.释放连接
                            transactionManager.release();
                        }
                    }
                });
    }
}

2.配置依赖注入

<!--配置代理的service对象-->
    <bean id="proxyAccountService" factory-bean="beanfactory" factory-method="getAccountService"></bean>

    <!--配置beanfactory-->
    <bean id="beanfactory" class="com.itheima.factory.BeanFactory">
        <!--注入service-->
        <property name="accountService" ref="accountService"></property>
        <!--注入事务管理器-->
        <property name="transactionManager" ref="transactionManger"></property>
    </bean>

3.service层与测试类

当我们改造完成之后,业务层用于控制事务的重复代码就都可以删掉了。

/**
 * 账户的业务层实现类
 */
public class AccountServiceImpl implements IAccountService{

    private IAccountDao accountDao;


    public void setAccountDao(IAccountDao accountDao) {
        this.accountDao = accountDao;
    }
    
	@Override
    public void transfer(String souceName, String targetName, Float money) {
        System.out.println("transfer......");
        //2.执行操作
            //1.根据名称查询转出账户
            Account source = accountDao.findAccountByName(souceName);
            //2.根据名称查询转入账户
            Account target = accountDao.findAccountByName(targetName);
            //3.转出账户减钱
            source.setMoney(source.getMoney() - money);
            //4.转入账户加钱
            target.setMoney(target.getMoney() + money);
            //5.更新转出账户
            accountDao.updateAccount(source);
            //6.更新转入账户
            accountDao.updateAccount(target);
        }
}        

所以测试类中:

/**
 * 使用Junit单元测试:测试我们的配置
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:bean.xml")
public class AccountServiceTest {

    @Autowired
    @Qualifier("proxyAccountService")//使用BeanFactory中的代理AccountService来执行
    private  IAccountService as;

    @Test
    public void testTransfer() {
        as.transfer("aaa","bbb", 100f);
    }
}
发布了47 篇原创文章 · 获赞 18 · 访问量 4871

猜你喜欢

转载自blog.csdn.net/qq_43605085/article/details/99353526