手写IOC和AOP(四)----动态代理改造事务控制

代理模式分为静态代理和动态代理,我们先以一个例子来回顾这两种代理模式的使用。
静态代理:
静态代理,有点类似于装饰设计模式。静态代理是一种编译期增强,还没运行就已经知道增强的目标对象。装饰设计模式是运行时增强,只有运行时才知道具体增强的目标。
以租房举例,我们可以自己去找房源去租房子,也可以通过中介去租,这里的我们也就是租房的客户就是委托者,中介就是一个代理者。作为客户,我们需要将自己的需求告知中介,作为中介,需要提供一个客户访问或接入的方式,得需要客户能找到你。站着两者的角度,他们最终目的都需要干一件事----租房。

/**
 * 租房接口
 */
public interface IRentingHouse {

    void rentingHouse();
}

这里的IRentingHouseImpl 就是一个委托者,需求是租房。

public class IRentingHouseImpl implements IRentingHouse {
    @Override
    public void rentingHouse() {
        System.out.println("我要租一间两室一厅的房子");
    }
}

这里的IRentingHouseProxy 就是一个代理类,他帮委托者干的活就是租房,所以也要实现IRentingHouse接口,实现rentingHouse方法。不同的是,在代理类中,通过有参构造提供了委托者接入的方式,获取到委托者对象后,在自己的租房方法中调用委托者中的原方法,实现了委托者的功能,又可以在其前后进行增强改造。

public class IRentingHouseProxy implements IRentingHouse{
    private IRentingHouse rentingHouse;
    public IRentingHouseProxy(IRentingHouse rentingHouse){
        this.rentingHouse = rentingHouse;
    }
    @Override
    public void rentingHouse() {
        //增强逻辑
        System.out.println("中介收取中介费");
        //调用原业务逻辑
        rentingHouse.rentingHouse();
        //原逻辑后增强
        System.out.println("中介将客户信息卖出");
    }
}

在委托者租房时,就可以不必自己直接调用自己的租房方法,而是将自己的需求告知代理类,租房子的事情就不需要管了。

public class Test {
    public static void main(String[] args) {
        IRentingHouseImpl rentingHouse = new IRentingHouseImpl();
        //自己租房子
//        rentingHouse.rentingHouse();
        //将自己需求告知对应的代理人,通过代理来租房子
        IRentingHouseProxy proxy = new IRentingHouseProxy(rentingHouse);
        proxy.rentingHouse();
    }
}

要想使用静态代理模式,我们必须自己去写一个代理类,代理类要实现和委托类相同的接口。而动态代理就不需要我们去写代理类了,代理类可以在底层通过反射自动生成。下面介绍下JDK动态代理和Cglib动态代理的使用。
JDK动态代理:
JDK动态代理的实现是由JDK自带的API中的Proxy.newProxyInstance来实现的。IRentingHouse 接口和IRentingHouseImpl 类复用前面提到的,这里不再叙述。

/**
 * 采用jdk动态代理来实现
 */
public class JDKProxy {
    public static void main(String[] args) {
        IRentingHouse iRentingHouse = new IRentingHouseImpl();
        //通过jdk提供的Proxy获取代理对象
        Object proxy = Proxy.newProxyInstance(iRentingHouse.getClass().getClassLoader(), iRentingHouse.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object result = null;
                System.out.println(method);//public abstract void com.nanmao.demo.dynamicproxy.IRentingHouse.rentingHouse()
                System.out.println(method.getName());//rentingHouse
                if("rentingHouse".equals(method.getName())){
                    //增强逻辑
                    System.out.println("中介收取中介费");
                    //调用原业务逻辑
                    result = method.invoke(iRentingHouse, args);
                    //原逻辑后增强
                    System.out.println("中介将客户信息卖出");
                }
                return result;
            }
        });
        //通过代理对象来调用原逻辑方法
        ((IRentingHouse)proxy).rentingHouse();
    }
}

Cglib动态代理:
要用Cglib动态代理的话,首先要导入cglib的依赖包。实现过程和JDK动态代理类似,通过net.sf.cglib.proxy.Enhancer包下的Enhancer.create来实现。通过实现代码可以发现,与JDK动态代理很大的不同点是,Cglib不需要委托类必须有对应的父接口,而在Proxy.newProxyInstance方法中要传入委托类的接口类型,这样看来,Cglib的应用范围更广一些。

        <!--引入cglib依赖包-->
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.1_2</version>
        </dependency>
public class CglibProxy {
    public static void main(String[] args) {
        IRentingHouse iRentingHouse = new IRentingHouseImpl();
        Object proxy = Enhancer.create(iRentingHouse.getClass(), new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            //参数中的Object[] objects表示的是方法的参数,相当于invoke中的args
                Object result = null;
                if ("rentingHouse".equals(method.getName())) {
                    //增强逻辑
                    System.out.println("中介收取中介费");
                    //调用原业务逻辑
                    result = method.invoke(iRentingHouse, objects);
                    //原逻辑后增强
                    System.out.println("中介将客户信息卖出");
                }
                return result;
            }
        });
        ((IRentingHouse)proxy).rentingHouse();
    }
}

动态代理改造事务控制:
将两种动态代理方式整合成一个代理工厂。在转账案例中,我们要对事务控制采用动态代理来实现,所以我们将原转账业务前后的try…catch部分移到代理工厂中,原业务还是在service层保持不动。

public class ProxyFactory {
    private ProxyFactory() {
    }
    
    public static ProxyFactory proxyFactory = new ProxyFactory();

    public static ProxyFactory getInstance() {
        return proxyFactory;
    }

    public Object getJdkProxy(Object clientObj) {
        //通过jdk提供的Proxy获取代理对象
        return Proxy.newProxyInstance(clientObj.getClass().getClassLoader(), clientObj.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object result = null;
                if ("transfer".equals(method.getName())) {
                    try {
                        //业务增强:开启事务
                        TransactionManager.getInstance().startTransaction();
                        //调用原转账业务
                        result = method.invoke(clientObj, args);
                        //业务增强:提交事务
                        TransactionManager.getInstance().commit();
                    } catch (Exception e) {
                        //业务增强:回滚事务
                        TransactionManager.getInstance().rollBack();
                        e.printStackTrace();
                        //抛出异常便于上层servlet捕获
                        throw e;
                    }
                }
                return result;
            }
        });
    }

    public Object getCglibProxy(Object clientObj) {
        return Enhancer.create(clientObj.getClass(), new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                Object result = null;
                if ("transfer".equals(method.getName())) {
                    try {
                        //业务增强:开启事务
                        TransactionManager.getInstance().startTransaction();
                        //调用原转账业务
                        result = method.invoke(clientObj, objects);
                        //业务增强:提交事务
                        TransactionManager.getInstance().commit();
                    } catch (Exception e) {
                        //业务增强:回滚事务
                        TransactionManager.getInstance().rollBack();
                        e.printStackTrace();
                        //抛出异常便于上层servlet捕获
                        throw e;
                    }
                }
                return result;
            }
        });
    }
}

注释掉TransferServiceImpl中对事务的处理,保留最初的业务代码即可。

    @Override
    public void transfer(String fromCardId, String toCardId, int money) throws Exception {
      /*  try {
            //开启事务
            TransactionManager.getInstance().startTransaction();*/

            Account fromAcctInfo  = accountDao.queryAcctByCardId(fromCardId);
            Account toAcctInfo = accountDao.queryAcctByCardId(toCardId);
            fromAcctInfo.setMoney(fromAcctInfo.getMoney() - money);
            toAcctInfo.setMoney(toAcctInfo.getMoney() + money);
            accountDao.updateAcctByCardId(fromAcctInfo);
            int i = 1 / 0;
            accountDao.updateAcctByCardId(toAcctInfo);
       /*     //提交事务
            TransactionManager.getInstance().commit();
        } catch (Exception e) {
            //回滚事务
            TransactionManager.getInstance().rollBack();
            e.printStackTrace();
            //抛出异常便于上层servlet捕获
            throw e;
        }*/
    }

TransferServlet 中,将获得的transferService 对象传入代理工厂中获取对应的代理对象,由代理对象执行后面操作。

public class TransferServlet extends HttpServlet {
//    private TransferService transferService = new TransferServiceImpl();
//    private TransferService transferService = (TransferServiceImpl)BeanFactory.getBean("transferService");

    TransferService transferService = (TransferService)ProxyFactory.newInstance().getJdkProxy(BeanFactory.getBean("transferService"));

代码改造:使用IOC实现对工具类的管理
我们这里对ConnectionUtils和TransactionManager两个工具类和ProxyFactory工厂,使用xml进行管理。首先,分析下这三者间的引用关系,如下图:
ConnectionUtils被TransactionManager和AccountDaoImpI引用,所以我们需要在这两者的bean标签下添加ConnectionUtils属性,在这两者代码中通过set方法对属性赋值。
TransactionManager又被ProxyFactory引用,所以要在ProxyFactory的bean标签下添加TransactionManager属性标签,在ProxyFactory代码中通过set方法对属性赋值。
在这里插入图片描述
修改后的bean.xml文件:

<?xml version="1.0" encoding="UTF-8" ?>
<beans>
    <bean id="accountDao" class="com.nanmao.dao.impl.AccountDaoImpl">
        <property name="ConnectionUtils" ref="connectionUtils"></property>
    </bean>
    <bean id="transferService" class="com.nanmao.service.impl.TransferServiceImpl">
        <!-- set + name 找到对应的set方法,将ref中对应的对象set进去-->
        <property name="AccountDao" ref="accountDao"></property>
    </bean>

    <!--添加新增的三个bean组件-->
    <!--连接管理器-->
    <bean id="connectionUtils" class="com.nanmao.utils.ConnectionUtils"></bean>
    <!--事务管理器-->
    <bean id="transactionManager" class="com.nanmao.utils.TransactionManager">
        <property name="ConnectionUtils" ref="connectionUtils"></property>
    </bean>
    <!--代理工厂-->
    <bean id="proxyFactory" class="com.nanmao.factory.ProxyFactory">
        <property name="TransactionManager" ref="transactionManager"></property>
    </bean>
</beans>

我们之前写的工具类和工厂类,都是单例模式的,但是我们需要用set方法对其赋值,所以要注释掉之前的单例部分。并且添加对应的成员变量和对应的set方法。之前我们用工厂类(或工具类)调用内部的newInstance方法产生对象,现在我们可以直接使用成员变量就可以了,在BeanFactory中会根据xml配置利用反射产生对应的对象。

TransactionManager :注释之前的单例部分,添加connectionUtils属性及set方法,将之前的ConnectionUtils.getInstance()创建工具类对象调用对应方法改成直接通过connectionUtils属性调用。

/**
 * 事务管理器:用于手动管理事务的开启、提交、回滚
 */
public class TransactionManager {
    private ConnectionUtils connectionUtils;

    public void setConnectionUtils(ConnectionUtils connectionUtils) {
        this.connectionUtils = connectionUtils;
    }
//        private TransactionManager() {
//    }
//
//    private static TransactionManager transactionManager = new TransactionManager();
//
//    //饿汉式实现单例
//    public static TransactionManager getInstance() {
//        return transactionManager;
//    }

    public void startTransaction() throws SQLException {
        //关闭JDBC中自动事务提交
        connectionUtils.getCurConnection().setAutoCommit(false);
    }

    public void commit() throws SQLException {
        connectionUtils.getCurConnection().commit();
    }

    public void rollBack() throws SQLException {
        connectionUtils.getCurConnection().rollback();
    }
}

AccountDaoImpl :添加connectionUtils属性及set方法,将之前的ConnectionUtils.getInstance()创建工具类对象调用对应方法改成直接通过connectionUtils属性调用。

public class AccountDaoImpl implements AccountDao {
    private ConnectionUtils connectionUtils;

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

    @Override
    public Account queryAcctByCardId(String cardId) throws Exception {
        //使用阿里druid连接池,获取连接
//        Connection connection = DruidUtils.getInstance().getConnection();
        //修改为:从当前线程中获取connection
//        Connection connection = ConnectionUtils.getInstance().getCurConnection();
        Connection connection = connectionUtils.getCurConnection();

        String sql = "select * from account where cardId = ?";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        preparedStatement.setString(1, cardId);
        ResultSet resultSet = preparedStatement.executeQuery();

        Account account = new Account();
        while (resultSet.next()) {
            account.setName(resultSet.getString("cardId"));
            account.setMoney(resultSet.getInt("money"));
            account.setCardId(resultSet.getString("cardId"));
        }
        //关闭连接,归还连接到连接池
        resultSet.close();
        preparedStatement.close();
        //连接不可以关闭,否则下次调用的时候就又需要创建新的连接
//        connection.close();

        return account;
    }

    @Override
    public int updateAcctByCardId(Account account) throws Exception {
        //使用阿里druid连接池,获取连接
//        Connection connection = DruidUtils.getInstance().getConnection();
        //修改为:从当前线程中获取connection
//        Connection connection = ConnectionUtils.getInstance().getCurConnection();
        Connection connection = connectionUtils.getCurConnection();

        String sql = "update account set money = ? where cardId = ?";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        preparedStatement.setInt(1, account.getMoney());
        preparedStatement.setString(2, account.getCardId());
        int result = preparedStatement.executeUpdate();

        //关闭连接,归还连接到连接池
        preparedStatement.close();
        //连接不可以关闭,否则下次调用的时候就又需要创建新的连接
//        connection.close();

        return result;
    }
}

ProxyFactory :添加transactionManager属性及set方法,将之前的TransactionManager .getInstance()创建工厂类对象调用对应方法改成直接通过transactionManager属性调用。

public class ProxyFactory {
    private TransactionManager transactionManager;

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

//        private ProxyFactory() {
//    }
//
//    public static ProxyFactory proxyFactory = new ProxyFactory();
//
//    public static ProxyFactory getInstance() {
//        return proxyFactory;
//    }

    public Object getJdkProxy(Object clientObj) {
        //通过jdk提供的Proxy获取代理对象
        return Proxy.newProxyInstance(clientObj.getClass().getClassLoader(), clientObj.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object result = null;
                if ("transfer".equals(method.getName())) {
                    try {
                        //业务增强:开启事务
                        transactionManager.startTransaction();
                        //调用原转账业务
                        result = method.invoke(clientObj, args);
                        //业务增强:提交事务
                        transactionManager.commit();
                    } catch (Exception e) {
                        //业务增强:回滚事务
                        transactionManager.rollBack();
                        e.printStackTrace();
                        //抛出异常便于上层servlet捕获
                        throw e;
                    }
                }
                return result;
            }
        });
    }

    public Object getCglibProxy(Object clientObj) {
        return Enhancer.create(clientObj.getClass(), new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                Object result = null;
                if ("transfer".equals(method.getName())) {
                    try {
                        //业务增强:开启事务
                        transactionManager.startTransaction();
                        //调用原转账业务
                        result = method.invoke(clientObj, objects);
                        //业务增强:提交事务
                        transactionManager.commit();
                    } catch (Exception e) {
                        //业务增强:回滚事务
                        transactionManager.rollBack();
                        e.printStackTrace();
                        //抛出异常便于上层servlet捕获
                        throw e;
                    }
                }
                return result;
            }
        });
    }
}

TransferServlet:proxyFactory 对象由原来的ProxyFactory.newInstance()获取,改为通过BeanFactory读取xml配置获取。

@WebServlet(name = "transferServlet", urlPatterns = "/transferServlet")
public class TransferServlet extends HttpServlet {
//    private TransferService transferService = new TransferServiceImpl();
//    private TransferService transferService = (TransferServiceImpl)BeanFactory.getBean("transferService");

//	  TransferService transferService = (TransferService)ProxyFactory.newInstance().getJdkProxy(BeanFactory.getBean("transferService"));

    //从BeanFactory获取ProxyFactory代理工厂对象,然后通过proxyFactory获取代理对象
    private ProxyFactory proxyFactory = (ProxyFactory) BeanFactory.getBean("proxyFactory");
    TransferService transferService = (TransferService)proxyFactory.getJdkProxy(BeanFactory.getBean("transferService"));

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 设置请求体的字符编码
        req.setCharacterEncoding("UTF-8");

        String fromCardId = req.getParameter("fromCardId");
        String toCardId = req.getParameter("toCardId");
        String moneyStr = req.getParameter("money");

        int money = Integer.parseInt(moneyStr);

        Result result = new Result();

        try {

            // 调用service层方法
            transferService.transfer(fromCardId, toCardId, money);
            result.setStatus("200");
        } catch (Exception e) {
            e.printStackTrace();
            result.setStatus("300");
            result.setMessage(e.toString());
        }

        // 响应
        resp.setContentType("application/json;charset=utf-8");
        resp.getWriter().print(JsonUtils.object2Json(result));

    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值