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

Spring 代理模式應用場景

1. 前言

大家好,我們學習了代理模式的概念,也知道了代理模式可以在程序的運行過程中,實現(xiàn)對某個方法的增強。那么,在我們程序的編寫過程中,

什么樣的場景,能使用代理模式呢?

本節(jié),我們模擬一個實際應用場景,目的是觀察日常程序中可能發(fā)生的問題,以及代理模式如何解決問題,這樣可以更加深刻地理解代理模式的意義。

2. 案例實戰(zhàn)

2.1 轉賬工程的搭建

我們模擬一個實際生活中常見的情景,就是賬號的轉賬。 假設有兩個用戶 A 和 用戶 B,我們通過程序,從 A 賬號中轉成指定的 money 到 B 賬號中。

那么,針對正常和異常的程序執(zhí)行,我們來分析下問題以及它的解決方案。

2.1.1 工程準備

創(chuàng)建 maven 工程

圖片描述

引入 pom 文件的依賴 jar 包坐標信息

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>commons-dbutils</groupId>
        <artifactId>commons-dbutils</artifactId>
        <version>1.4</version>
    </dependency>

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.6</version>
    </dependency>

    <dependency>
        <groupId>c3p0</groupId>
        <artifactId>c3p0</artifactId>
        <version>0.9.1.2</version>
    </dependency>

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
</dependencies>

Spring 框架的配置文件編寫

<?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="queryRunner"></property>
        <!-- 注入ConnectionUtils -->
        <property name="connectionUtils" ref="connectionUtils"></property>
    </bean>
    <!--配置QueryRunner-->
    <bean id="queryRunner" 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>
</beans>

配置文件說明:

  1. connectionUtils 是獲取數(shù)據(jù)庫連接的工具類;
  2. dataSource 采用 c3p0 數(shù)據(jù)源,大家一定要注意數(shù)據(jù)庫的名稱與賬號名和密碼;
  3. queryRunner 是 dbutils 第三方框架提供用于執(zhí)行 SQL 語句,操作數(shù)據(jù)庫的一個工具類;
  4. accountDao 和 accountService 是我們自定義的業(yè)務層實現(xiàn)類和持久層實現(xiàn)類。

項目使用數(shù)據(jù)庫環(huán)境

CREATE TABLE account (
id int(11) NOT NULL auto_increment,
accountNum varchar(20) default NULL,
money int(8) default NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8

圖片描述

2.1.2 代碼編寫

實體類代碼

public class Account implements Serializable {
    //數(shù)據(jù)id
    private Integer id;
    //賬號編碼
    private String accountNum;
    //賬號金額
    private Float money;
    //省略 get 和 set 的方法

}

持久層接口

//接口代碼
public interface IAccountDao {

    /**
     * 更新
     * @param account
     */
    void updateAccount(Account account);

    /**
     * 根據(jù)編號查詢賬戶
     * @param accountNum
     * @return  如果有唯一的一個結果就返回,如果沒有結果就返回null
     *          如果結果集超過一個就拋異常
     */
    Account findAccountByNum(String accountNum);
}

持久層實現(xiàn)類

public class AccountDaoImpl implements IAccountDao {
	//數(shù)據(jù)庫查詢工具類
    private QueryRunner runner;
	//數(shù)據(jù)庫連接工具類
    private ConnectionUtils connectionUtils;
    //省略 get 和 set 的方法
   
    
    //修改賬號的方法
    public void updateAccount(Account account) {
        try{
            runner.update(connectionUtils.getThreadConnection(),
                          "update account set accountNum=?,money=? where id=?",account.getAccountNum(),account.getMoney(),account.getId());
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
	//根據(jù)賬號查詢 Account 對象的方法
    public Account findAccountByNum(String accountNum) {
        try{
            List<Account> accounts = runner.query(connectionUtils.getThreadConnection(),
                                                  "select * from account where accountNum = ? ",new BeanListHandler<Account>(Account.class),accountNum);
            if(accounts == null || accounts.size() == 0){
                return null;
            }
            if(accounts.size() > 1){
                throw new RuntimeException("結果集不唯一,數(shù)據(jù)有問題");
            }
            return accounts.get(0);
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

業(yè)務層接口

public interface IAccountService {
    /**
     * 轉賬
     * @param sourceAccount        轉出賬戶名稱
     * @param targetAccount        轉入賬戶名稱
     * @param money             轉賬金額
     */
    void transfer(String sourceAccount, String targetAccount, Integer money);

}

業(yè)務層實現(xiàn)類

public class AccountServiceImpl implements IAccountService {
	//持久層對象
    private IAccountDao accountDao;
    //省略 set 和 get 方法

    //轉賬的方法
    public void transfer(String sourceAccount, String targetAccount, Integer money) {
		//查詢原始賬戶
        Account source = accountDao.findAccountByNum(sourceAccount);
        //查詢目標賬戶
        Account target = accountDao.findAccountByNum(targetAccount);
        //原始賬號減錢
        source.setMoney(source.getMoney()-money);
        //目標賬號加錢
        target.setMoney(target.getMoney()+money);
        //更新原始賬號
        accountDao.updateAccount(source);
        //更新目標賬號
        accountDao.updateAccount(target);
        System.out.println("轉賬完畢");

    }
}

測試運行類代碼

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:bean.xml")
public class AccountServiceTest {
    
    @Autowired
    @Qualifier("proxyAccountService")
    private  IAccountService as;

    @Test
    public  void testTransfer(){
        as.transfer("622200009999","622200001111",100);
    }
}

測試結果

圖片描述

代碼執(zhí)行完畢,可以看到輸出打印轉賬 ok 了。那么數(shù)據(jù)庫的數(shù)據(jù)有沒有改變呢?我們再看一眼:

圖片描述

可以看到:兩個賬號的數(shù)據(jù)已經(jīng)發(fā)生了改變,證明轉賬的動作,確實完成了。那這樣看來,我們的代碼也沒有問題啊,代理模式有什么用呢?

接下來我們改造下工程,模擬程序發(fā)生異常時候,執(zhí)行以后的結果如何。

2.1.3 改造業(yè)務類代碼

在業(yè)務層的代碼加入一行異常代碼,看看結果是否還會轉賬成功呢?

圖片描述

執(zhí)行結果:

圖片描述

當然了,其實提前也能想得到,肯定會執(zhí)行失敗的啦,哈哈哈哈,我們手動加了運算會出現(xiàn)異常的代碼嘛!但是轉賬的動作是不是也失敗了呢?我們再來看一下數(shù)據(jù)庫:

圖片描述

問題來了: id 為 1 的賬號 money 的列值由原來的 900 變成了 800,說明存款確實減少了 100,但是由于在代碼執(zhí)行的過程中,出現(xiàn)了異常,導致原始賬號減少 100 的金錢后保存成功, 而 id 為 2 的賬號并沒有增加 100。這就出現(xiàn)了數(shù)據(jù)的事務問題,破壞了數(shù)據(jù)的原子性和一致性。

那么如何解決呢? 思路就是將我們的數(shù)據(jù)操作代碼,使用事務控制起來。由于本小節(jié)篇幅有限,我們留待下一小節(jié)解決。

3. 小結

本小節(jié)模擬了一個現(xiàn)實生活中轉賬的業(yè)務場景,其目的是為了引出我們的程序在執(zhí)行過程中可能會產(chǎn)生的問題。而如何解決,并且對于原始代碼侵入性更小,耦合性更低,是我們需要思考的事情。

使用知識點:

  1. JDBC 的基礎,本案例用到了 JDBC 連接數(shù)據(jù)庫的工具類 dbutils 知識;
  2. 數(shù)據(jù)庫基礎,本案例的數(shù)據(jù)操作,涉及到了事務的四個特性:原子性,一致性,隔離性,持久性。