第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

Spring 代理模式解決事務

1. 前言

大家好,本小節(jié),我們學習代理模式解決轉賬過程中產(chǎn)生的事務問題,如有中途而來的童鞋,請先移步上一小節(jié)學習下問題的場景。

2. 實戰(zhàn)案例

2.1 實現(xiàn)思路介紹

1. 創(chuàng)建一個工具類,目的是用于管理數(shù)據(jù)庫的事務,提供事務的開啟,提交,回滾等操作;

2. 創(chuàng)建一個代理處理器類,目的是生成轉賬實現(xiàn)類的代理對象,對轉賬的業(yè)務方法提供增強,主要是在數(shù)據(jù)操作之前,和操作之后干點事,嘿嘿嘿;

3. 在 Spring 的配置文件中,通過 xml 文件的標簽實例化管理事務的工具類和生成代理對象的處理器類。

2.2 代碼實現(xiàn)

1. 創(chuàng)建事務管理器類

package com.offcn.transaction;

/**
 * @Auther: wyan
 * @Date: 2020-05-26 21:20
 * @Description:
 */

import com.offcn.utils.ConnectionUtils;

/**
 * 和事務管理相關的工具類,它包含了,開啟事務,提交事務,回滾事務和釋放連接
 */
public class TransactionManager {
	//獲取數(shù)據(jù)庫連接的工具類
    private ConnectionUtils connectionUtils;

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

    /**
     * 開啟事務
     */
    public  void beginTransaction(){
        try {
            connectionUtils.getThreadConnection().setAutoCommit(false);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 提交事務
     */
    public  void commit(){
        try {
            connectionUtils.getThreadConnection().commit();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 回滾事務
     */
    public  void rollback(){
        try {
            connectionUtils.getThreadConnection().rollback();
        }catch (Exception e){
            e.printStackTrace();
        }
    }


    /**
     * 釋放連接
     */
    public  void release(){
        try {
            connectionUtils.getThreadConnection().close();//還回連接池中
            connectionUtils.removeConnection();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

代碼解釋:此工具類主要作用是對數(shù)據(jù)庫連接實現(xiàn)事務的開啟,提交以及回滾。至于何時開啟事務,何時提交事務,何時回滾事務,那就根據(jù)業(yè)務場景需要調用該類的方法即可。

2. 創(chuàng)建動態(tài)處理器

package com.offcn.utils;

import com.offcn.service.IAccountService;
import com.offcn.transaction.TransactionManager;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @Auther: wyan
 * @Date: 2020-05-26 21:08
 * @Description:
 */
public class TransactionProxyFactory {
	//被代理的業(yè)務類接口
    private IAccountService accountService;
	//提供事務管理的工具類
    private TransactionManager txManager;

    public void setTxManager(TransactionManager txManager) {
        this.txManager = txManager;
    }

    public final void setAccountService(IAccountService accountService) {
        this.accountService = accountService;
    }
    /**
     * 獲取Service代理對象
     * @return
     */
    public IAccountService getAccountService() {
        return (IAccountService) Proxy.newProxyInstance(accountService.getClass().getClassLoader(),
                accountService.getClass().getInterfaces(),
                new InvocationHandler() {
                    /**
                     * 添加事務的支持
                     *
                     * @param proxy
                     * @param method
                     * @param args
                     * @return
                     * @throws Throwable
                     */
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                       // 
                        Object rtValue = null;
                        try {
                            //1.開啟事務
                            txManager.beginTransaction();
                            //2.執(zhí)行操作
                            rtValue = method.invoke(accountService, args);
                            //3.提交事務
                            txManager.commit();
                            //4.返回結果
                            return rtValue;
                        } catch (Exception e) {
                            //5.回滾操作
                            txManager.rollback();
                            throw new RuntimeException(e);
                        } finally {
                            //6.釋放連接
                            txManager.release();
                        }
                    }
                });

    }
}

代碼解釋:

此類的核心代碼就是 getAccountService 方法,該方法返回代理業(yè)務類示例,而在代理對象的 invoke 方法內部,實現(xiàn)對原始被代理對象的增強。

方法的參數(shù)解釋如下:

  1. proxy: 該參數(shù)就是被代理的對象實例本身;
  2. method: 該參數(shù)是被代理對象正在執(zhí)行的方法對象;
  3. args: 該參數(shù)是正在訪問的方法參數(shù)對象。

在方法內部,method.invoke() 的方法調用,即表示被代理業(yè)務類的方法執(zhí)行,我們調用 txManager 的開啟事務方法。在 method.invoke() 方法執(zhí)行之后,調用提交事務的方法。

一旦執(zhí)行過程出現(xiàn)異常,在 catch 代碼塊中調用事務回滾的方法。這樣就保證了事務的原子性,執(zhí)行的任務,要么全部成功,要么全部失敗。

最終在 finally 的代碼塊中,調用釋放連接的方法。

3. 配置文件的修改:

添加事務管理的相關配置,完整配置文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 配置Service -->
    <bean id="accountService" class="com.offcn.service.impl.AccountServiceImpl">
        <!-- 注入dao -->
        <property name="accountDao" ref="accountDao"></property>
    </bean>
    <!--配置Dao對象-->
    <bean id="accountDao" class="com.offcn.dao.impl.AccountDaoImpl">
        <!-- 注入QueryRunner -->
        <property name="runner" ref="runner"></property>
        <!-- 注入ConnectionUtils -->
        <property name="connectionUtils" ref="connectionUtils"></property>
    </bean>
    <!--配置QueryRunner-->
    <bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype"></bean>
    <!-- 配置數(shù)據(jù)源 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!--連接數(shù)據(jù)庫的必備信息-->
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/transmoney"></property>
        <property name="user" value="root"></property>
        <property name="password" value="root"></property>
    </bean>
    <!-- 配置Connection的工具類 ConnectionUtils -->
    <bean id="connectionUtils" class="com.offcn.utils.ConnectionUtils">
        <!-- 注入數(shù)據(jù)源-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- 配置事務管理器-->
    <bean id="txManager" class="com.offcn.transaction.TransactionManager">
        <!-- 注入ConnectionUtils -->
        <property name="connectionUtils" ref="connectionUtils"></property>
    </bean>
    <!--配置beanfactory-->
    <bean id="beanFactory" class="com.offcn.utils.TransactionProxyFactory">
        <!-- 注入service -->
        <property name="accountService" ref="accountService"></property>
        <!-- 注入事務管理器 -->
        <property name="txManager" ref="txManager"></property>
    </bean>
    <!--配置代理的service-->
    <bean id="proxyAccountService" factory-bean="beanFactory" factory-method="getAccountService"></bean>

</beans>

4. 測試類代碼

圖片描述

代碼解釋:

本測試代碼發(fā)生一個小變化,第 23 行的位置,多了一個注解 @Qualifier 。此注解的作用不知各位是否還記得,如果在 Spring 的容器中,出現(xiàn)多種同類型的 bean ,可以通過此注解指定引入的

實例,所以這里的 注解內的字符串 proxyAccountService 表示本 IAccountService 接口引入的實例為代理對象。那么為什么要引入代理對象呢?因為代理對象的方法內部已經(jīng)做了增強邏輯,通過 TransactionManager 類實現(xiàn)對事務的開啟,提交和回滾。

5. 測試結果:

為了測試效果更明顯,我們先把數(shù)據(jù)庫的數(shù)據(jù)還原為每人各 1000,如圖:

圖片描述

執(zhí)行代碼后結果:

圖片描述

當然還會繼續(xù)報錯,但是數(shù)據(jù)庫呢?上次是一個賬號減去了 100 塊錢,另外一個賬號卻沒有增加錢,這次我們來看看:

圖片描述

可以看到:賬號的金錢依然是原樣,這就說明事務的控制已經(jīng)生效了,保證了數(shù)據(jù)的一致性。

3. 小結

本小節(jié)學習了代理模式實現(xiàn)對事務的控制,加深了代理模式的優(yōu)點及作用:

  1. 職責清晰: 代理類與被代理類各司其職,互不干擾;
  2. 高擴展性: 代碼耦合性低,可以更加方便對方法做增強;
  3. 符合開閉原則: 系統(tǒng)具有較好的靈活性和可擴展性。