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

首頁 慕課教程 Spring 入門教程 Spring 入門教程 Spring AOP 實(shí)現(xiàn)之 XML 配置

Spring AOP 實(shí)現(xiàn)之 XML 配置

1. 前言

大家好,本小節(jié),我們學(xué)習(xí) Spring 框架中基于代理模式實(shí)現(xiàn)的 AOP。關(guān)于什么是代理模式,我們在前兩個(gè)小節(jié)已經(jīng)詳細(xì)介紹過概念,并演示了代理模式的使用。

可能大家也有了一些體會,我們可以使用代理模式來對我們的一些功能方法做增強(qiáng)。只不過有一些不如人意的地方:

  • 自定義代理模式代碼編寫過于臃腫
  • 侵入性比較強(qiáng),代碼不夠優(yōu)雅
  • 控制事務(wù)的實(shí)現(xiàn)過于繁瑣。

疑問導(dǎo)出:

如何簡單輕便優(yōu)雅地解決這種問題呢?當(dāng)然就是我們的主角 Spring 的 AOP 啦。

對于 AOP ,我們也已經(jīng)詳細(xì)解釋過它的概念,對于 Spring 框架中的 AOP 實(shí)例,就在本小節(jié)做一個(gè)簡單的實(shí)現(xiàn)。

2. 實(shí)例演示

2.1 工程搭建介紹

數(shù)據(jù)庫表結(jié)構(gòu):

建表 SQL 語句如下:

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

圖片描述

工程代碼介紹:

  1. 實(shí)體類: 跟數(shù)據(jù)庫表對應(yīng)的 Java 類 Account ;
  2. 操作實(shí)體類的: Dao 和 Dao 的接口實(shí)現(xiàn)類 ;
  3. 調(diào)用持久層的業(yè)務(wù)類: Service 和 Service 的實(shí)現(xiàn)類 ;
  4. 事務(wù)管理器類: TransactionManager 提供事務(wù)的一系列操作 ;
  5. 測試代碼類: 初始化 Spring 調(diào)用類中的方法測試 。

思路介紹

本測試案例同前兩個(gè)小節(jié)實(shí)現(xiàn)的目的完全一致,不同的在于本小節(jié)使用 Spring 的 AOP 替代代理類。先回顧下 AOP 中的核心概念:

圖片描述

所以:對原始業(yè)務(wù)類中的方法增強(qiáng)行為也就是 Spring 的 AOP 中所謂的前置通知,在對原始業(yè)務(wù)類中的方法執(zhí)行之后的增強(qiáng)行為就是后置通知。

而一旦出現(xiàn)異常,那么所做的動作就是異常通知。本案例使用幾種通知,來實(shí)現(xiàn)事務(wù)的控制。

2.2 代碼實(shí)現(xiàn)

1. 創(chuàng)建 maven 工程:

圖片描述

pom 文件的 jar 包坐標(biāo)如下:

<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>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.8.7</version>
    </dependency>
</dependencies>

2. 實(shí)體類 Account

public class Account implements Serializable {
    //數(shù)據(jù)id
    private Integer id;
    //賬號編碼
    private String accountNum;
    //賬號金額
    private Float money;

    //省略get 和set 方法
}

3. 數(shù)據(jù)庫連接工具類

public class ConnectionUtils {

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

    private DataSource dataSource;

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

    /**
     * 獲取當(dāng)前線程上的連接
     * @return
     */
    public Connection getThreadConnection() {
        try{
            //1.先從ThreadLocal上獲取
            Connection conn = tl.get();
            //2.判斷當(dāng)前線程上是否有連接
            if (conn == null) {
                //3.從數(shù)據(jù)源中獲取一個(gè)連接,并且存入ThreadLocal中
                conn = dataSource.getConnection();
                tl.set(conn);
            }
            //4.返回當(dāng)前線程上的連接
            return conn;
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }

    /**
     * 把連接和線程解綁
     */
    public void removeConnection(){
        tl.remove();
    }
}

4. 持久層 dao 和 dao 的 實(shí)現(xiàn)類:

//dao的接口
public interface IAccountDao {
    /**
     * 更新
     * @param account
     */
    void updateAccount(Account account);
    /**
     * 根據(jù)編號查詢賬戶
     */
    Account findAccountByNum(String accountNum);
}
//dao的實(shí)現(xiàn)類
public class AccountDaoImpl implements IAccountDao {
    //dbutil的查詢工具類
    private QueryRunner runner;
    //連接的工具類
    private ConnectionUtils connectionUtils;

    public void setRunner(QueryRunner runner) {
        this.runner = runner;
    }

    public void setConnectionUtils(ConnectionUtils connectionUtils) {
        this.connectionUtils = connectionUtils;
    }
   
    //修改賬號
    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ù)賬號查詢
    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("結(jié)果集不唯一,數(shù)據(jù)有問題");
            }
            return accounts.get(0);
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

5. 業(yè)務(wù)類 Service 和 Service 的實(shí)現(xiàn)類

//業(yè)務(wù)接口
public interface IAccountService {
    /**
     * 轉(zhuǎn)賬
     * @param sourceAccount        轉(zhuǎn)出賬戶名稱
     * @param targetAccount        轉(zhuǎn)入賬戶名稱
     * @param money             轉(zhuǎn)賬金額
     */
    void transfer(String sourceAccount, String targetAccount, Integer money);

}
//業(yè)務(wù)實(shí)現(xiàn)類
public class AccountServiceImpl implements IAccountService {
	//持久層對象
    private IAccountDao accountDao;
    //省略 set 和 get 方法

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

    }
}

6. 事務(wù)管理器類

package com.offcn.transaction;
/**
 * @Auther: wyan
 * @Date: 2020-05-26 21:20
 * @Description:
 */
import com.offcn.utils.ConnectionUtils;

/**
 * 和事務(wù)管理相關(guān)的工具類,它包含了,開啟事務(wù),提交事務(wù),回滾事務(wù)和釋放連接
 */
public class TransactionManager {

    private ConnectionUtils connectionUtils;

    public void setConnectionUtils(ConnectionUtils connectionUtils) {
        this.connectionUtils = connectionUtils;
    }
    /**
     * 開啟事務(wù)
     */
    public  void beginTransaction(){
        try {
            System.out.println("開啟事務(wù)");
            connectionUtils.getThreadConnection().setAutoCommit(false);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    /**
     * 提交事務(wù)
     */
    public  void commit(){
        try {
            System.out.println("提交事務(wù)");
            connectionUtils.getThreadConnection().commit();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    /**
     * 回滾事務(wù)
     */
    public  void rollback(){
        try {
            System.out.println("回滾事務(wù)");
            connectionUtils.getThreadConnection().rollback();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    /**
     * 釋放連接
     */
    public  void release(){
        try {
            System.out.println("釋放連接");
            connectionUtils.getThreadConnection().close();//還回連接池中
            connectionUtils.removeConnection();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

代碼解釋:此工具類就作為 Spring 使用 AOP 管理事務(wù)的通知類,里面的各個(gè)方法用于配置 Spring 的通知使用。為了測試效果,在每個(gè)通知方法內(nèi),我們輸出打印了測試語句。

7. 配置文件中添加 AOP 的相關(guān)配置

<?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">
        <property name="accountDao" ref="accountDao"></property>
    </bean>
    <!--配置Dao對象-->
    <bean id="accountDao" class="com.offcn.dao.impl.AccountDaoImpl"> 
        <property name="runner" ref="runner"></property>
        <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>

	 <!-- 配置事務(wù)管理器-->
    <bean id="txManager" class="com.offcn.transaction.TransactionManager">
        <!-- 注入ConnectionUtils -->
        <property name="connectionUtils" ref="connectionUtils"></property>
    </bean>
	<!-- aop相關(guān)的節(jié)點(diǎn)配置 -->
	 <aop:config>
        <aop:pointcut expression="execution ( * com.offcn.service.*.*(..))" id="pc"/>
        <aop:aspect ref="txManager">
            <aop:before method="beginTransaction" pointcut-ref="pc"/>
            <aop:after-returning method="commit" pointcut-ref="pc"/>
            <aop:after method="release" pointcut-ref="pc"/>
            <aop:after-throwing method="rollback" pointcut-ref="pc"/>
        </aop:aspect>
    </aop:config>

</beans>

配置文件說明:

  1. connectionUtils: 是獲取數(shù)據(jù)庫連接的工具類;
  2. dataSource: 采用 c3p0 數(shù)據(jù)源,大家一定要注意數(shù)據(jù)庫的名稱與賬號名和密碼;
  3. queryRunner: dbutils 第三方框架提供用于執(zhí)行 SQL 語句,操作數(shù)據(jù)庫的一個(gè)工具類;
  4. accountDao 和 accountService: 是我們自定義的業(yè)務(wù)層實(shí)現(xiàn)類和持久層實(shí)現(xiàn)類;
  5. aop:config: 此節(jié)點(diǎn)是新增加 AOP 配置,AOP 相關(guān)信息都在這;
  6. aop:pointcut: 此節(jié)點(diǎn)是切入點(diǎn),表示哪些類的哪些方法在執(zhí)行的時(shí)候會應(yīng)用 Spring 配置的通知進(jìn)行增強(qiáng);
  7. aop:aspect: 此節(jié)點(diǎn)是配置切面類的節(jié)點(diǎn),在 AOP 介紹的小節(jié)解釋過,它的作用主要就是整合通知和切入點(diǎn)。
    null 前置、后置、異常、和最終??梢钥吹贸鰜?before 前置通知執(zhí)行的方法是開啟事務(wù), after-returning 成功執(zhí)行的方法是提交事務(wù),after 最終執(zhí)行的方法是釋放連接,after-throwing 出現(xiàn)異常執(zhí)行的方法是回滾。

8. 測試類代碼

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class AccountServiceTest {

    @Autowired
    private IAccountService accountService;

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

測試結(jié)果:

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

圖片描述

可以看到,我們通過在 xml 文件中配置 Spring 的 AOP 相關(guān)配置,就可以實(shí)現(xiàn)對我們業(yè)務(wù)類中的方法實(shí)現(xiàn)了增強(qiáng),無需自定義對業(yè)務(wù)類做代理實(shí)現(xiàn)。

3. 小結(jié)

本小節(jié)學(xué)習(xí)了 Spring 中 AOP 的使用,那么哪些要求大家掌握的呢?

  1. AOP 的相關(guān)概念,什么是切面,什么是通知,什么是切入點(diǎn);

  2. 通知的幾種類型,以及他們的執(zhí)行時(shí)機(jī);

  3. 如何在 Spring 配置文件中使用 xml 的方式實(shí)現(xiàn) AOP。