Spring總結(二)---AOP、事務

SpringAop相關概念

1.AOP概念

Aspect Oriented Programming面向切面編程,通過預編譯的方式和運行期動態代理實現程序功能的統一維護的一種技術。
AOP是OOP的延續,是軟件開發中的一個熱點,也是Spring框架中的一個重要內容,是函數式編程的一種衍生泛型。
利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各個部分之間的耦合度降低,提高程序的可重用性,同時提高了開發效率。
簡單的說,它就是把我們程序重複的代碼抽取出來,在需要執行的時候,使用動態代理技術,在不修改源碼的基礎上,對我們自己的已有方法進行增強。

2.作用及優勢

作用:在程序運行期間,不修改源碼對已有方法進行增強。

優勢:減少重複代碼、提高開發效率、維護方便

3.實現方式

動態代理

業務:轉賬
兩個賬戶,各有100元
轉出賬戶,轉出10元,-10元=90
轉入賬戶,收到10元,+10元=110
整個過程完整進行,沒有問題。如果轉賬過程被中斷了,就會出現,轉入、轉出賬戶不一致的問題。
此時應該讓整個轉賬過程在一個線程中,轉賬成功都成功,失敗都失敗。

示例代碼如下:
在這裏插入圖片描述

實現轉賬在一個線程下進行,但是有很多重複代碼
在這裏插入圖片描述
抽出公共代碼,解決代碼重複問題
在這裏插入圖片描述

  • 動態代理:
  特點:字節碼隨用隨創建,隨用隨加載
  作用:不修改源碼的基礎上對方法增強
  分類:
     基於接口的動態代理
      基於子類的動態代理
  基於子類的動態代理:
      涉及的類:Enhancer
      提供者:第三方cglib庫
 如何創建代理對象:
      使用Enhancer類中的create方法
  創建代理對象的要求:
      被代理類不能是最終類
  create方法的參數:
      Class:字節碼
         它是用於指定被代理對象的字節碼。

      Callback:用於提供增強的代碼
          它是讓我們寫如何代理。我們一般都是些一個該接口的實現類,通常情況下都是匿名內部類,但不是必須的。
          此接口的實現類都是誰用誰寫。
          我們一般寫的都是該接口的子接口實現類:MethodInterceptor

動態代理代碼示例:

生產廠家生產電腦(Producter),銷售商銷售電腦(IProducter),消費者(Client)進行購買電腦。
接口存在的意義:如果銷售商規模比較大,生產廠家規模小:銷售商會選擇,要銷售的電腦的標準(接口),生成廠家按照標準去生產(類)【大項目的時候,有接口更方便類職責的單一化】
如果銷售商規模比較小,生產廠家規模大:銷售商會按照廠家生產出來的產品進貨銷售,或者生產銷售一體化(類)【小項目,不使用接口,也會比較方便】
在這裏插入圖片描述
在這裏插入圖片描述

4.關於代理的選擇

在spring中,框架會更具目標類是否實現了接口來決定採用那種動態代理的方式。
基於接口的動態代理,要求被代理的對象,最少實現一個接口。
基於子類的動態代理,被代理類不能是最終類。

5.相關術語

(1)Joinpoint(連接點);

被攔截到的點,在spring中,這些點指的是方法,因爲spring只支持方法類型的連接點。

(2)pointcut(切入點):

我們要多哪些Joinpoint進行攔截的定義。
【所有的切入點,一定是連接點,但是不是所有的連接點,都是切入點(例如沒有被增強的連接點)】

(3)Advice(通知/增強):

攔截到Joinpoint之後所要做的事情就是通知。
通知的類型:前置通知、後置通知、異常通知、最終通知、環繞通知。

(4)Introduction(引介):

引介是一種特殊的通知在不修改類代碼的前提下,Introduction可以在運行期爲類動態地添加一些方法或Field。

(5)Target(目標對象)

代理的目標對象

(6)Weaving(織入):

把增強應用到目標對象來創建的代理對象的過程。
spring採用動態代理織入,而Aspect採用編譯期織入和類裝載期織入。

(7)Proxy(代理):

一個類被AOP織入增強後,就產生一個結果代理類。

(8)Aspect(切面):

是切入點和通知(引介)的結合。

6.AOP代碼示例—SpringAOP(SpringAOP)

spring中基於XML的AOP配置步驟

1、把通知Bean也交給spring來管理
2、使用aop:config標籤表明開始AOP的配置
3、使用aop:aspect標籤表明配置切面
        id屬性:是給切面提供一個唯一標識
        ref屬性:是指定通知類bean的Id。
4、在aop:aspect標籤的內部使用對應標籤來配置通知的類型
       我們現在示例是讓printLog方法在切入點方法執行之前之前:所以是前置通知
       aop:before:表示配置前置通知
            method屬性:用於指定Logger類中哪個方法是前置通知
            pointcut屬性:用於指定切入點表達式,該表達式的含義指的是對業務層中哪些方法增強

切入點表達式的寫法:

        關鍵字:execution(表達式)
        表達式:
            訪問修飾符  返回值  包名.包名.包名...類名.方法名(參數列表)
        標準的表達式寫法:
            public void com.dynamic.service.impl.AccountServiceImpl.saveAccount()
        訪問修飾符可以省略
            void com.dynamic.service.impl.AccountServiceImpl.saveAccount()
        返回值可以使用通配符,表示任意返回值
            * com.dynamic.service.impl.AccountServiceImpl.saveAccount()
        包名可以使用通配符,表示任意包。但是有幾級包,就需要寫幾個*.
            * *.*.*.*.AccountServiceImpl.saveAccount())
        包名可以使用..表示當前包及其子包
            * *..AccountServiceImpl.saveAccount()
        類名和方法名都可以使用*來實現通配
            * *..*.*()
        參數列表:
            可以直接寫數據類型:
                基本類型直接寫名稱           int
                引用類型寫包名.類名的方式   java.lang.String
            可以使用通配符表示任意類型,但是必須有參數
            可以使用..表示有無參數均可,有參數可以是任意類型
        全通配寫法:
            * *..*.*(..)
        實際開發中切入點表達式的通常寫法:
            切到業務層實現類下的所有方法
                * com.dynamic.service.impl.*.*(..)

在這裏插入圖片描述
運行結果
在這裏插入圖片描述

AOP代碼示例—AnnotationAOP(使用註解的AOP)

在這裏插入圖片描述
運行結果:
(環繞通知:有明確的切入點;其他通知:可能會出現順序錯亂的情況)

在這裏插入圖片描述
【不用註解時的bean.xml文件】

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!-- 配置srping的Ioc,把service對象配置進來-->
    <bean id="accountService" class="com.dynamic.service.impl.AccountServiceImpl"></bean>


    <!-- 配置Logger類 -->
    <bean id="logger" class="com.dynamic.utils.Logger"></bean>
    <!--配置AOP-->
    <aop:config>
        <!-- 配置切入點表達式 id屬性用於指定表達式的唯一標識。expression屬性用於指定表達式內容
              此標籤寫在aop:aspect標籤內部只能當前切面使用。
              它還可以寫在aop:aspect外面,此時就變成了所有切面可用
          -->
        <aop:pointcut id="pt1" expression="execution(* com.dynamic.service.impl.*.*(..))"></aop:pointcut>
        <!--配置切面 -->
        <aop:aspect id="logAdvice" ref="logger">
            <!-- 配置前置通知:在切入點方法執行之前執行
            <aop:before method="beforePrintLog" pointcut-ref="pt1" ></aop:before>-->
            <!-- 配置後置通知:在切入點方法正常執行之後值。它和異常通知永遠只能執行一個
            <aop:after-returning method="afterReturningPrintLog" pointcut-ref="pt1"></aop:after-returning>-->
            <!-- 配置異常通知:在切入點方法執行產生異常之後執行。它和後置通知永遠只能執行一個
            <aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pt1"></aop:after-throwing>-->
            <!-- 配置最終通知:無論切入點方法是否正常執行它都會在其後面執行
            <aop:after method="afterPrintLog" pointcut-ref="pt1"></aop:after>-->
            <!-- 配置環繞通知 詳細的註釋請看Logger類中-->
            <aop:around method="aroundPringLog" pointcut-ref="pt1"></aop:around>
        </aop:aspect>
    </aop:config>
</beans>

不適用XML的配置方式

@Configuration
@ComponentScan(basepackages="com.dynamic")
@EnableAspectJAutoProxy
public class SpringConfiguration {}

spring對事務的控制

1.JdbcTemplate概述

它是 spring 框架中提供的一個對象,是對原始 Jdbc API 對象的簡單封裝。spring 框架爲我們提供了很多的操作模板類。
操作關係型數據的:JdbcTemplate 、HibernateTemplate
操作 nosql 數據庫的:RedisTemplate
操作消息隊列的:JmsTemplate
我們在導包的時候,除了要導入spring-jdbc-5.0.2.RELEASE.jar 包外,還需要導入一個 spring-tx-5.0.2.RELEASE.jar(它是和事務相關的)

spring中使用事務有兩種方式,一種是編程式事務,一種是聲明式事務。編程式事務推薦使用TransactionTemplate,實現TransactionCallback接口,需要編碼實現;聲明式事務只需要在函數增加註解@Transactional,無需任何配置,代碼入侵較小,使用AOP原理,推薦使用聲明式事務,在應用啓動類上記得加上@EnableTransactionManagement註解喲。

2.JdbcTemplate代碼示例

(1)JdbcTemplate簡單應用

在這裏插入圖片描述

(2)JdbcTemplate在spring的ioc中使用

修改上一個((1)JdbcTemplate簡單應用)測試類的代碼
在這裏插入圖片描述

(3)JdbcTemplate的CRUD操作

在這裏插入圖片描述

(4)JdbcTemplate在Dao中的操作

在這裏插入圖片描述

(5)JdbcDaoSupport的使用以及Dao的兩種編寫方式

解決(4.JdbcTemplate在Dao中的操作)重複代碼dao可以繼承JdbcDaoSupport
在這裏插入圖片描述
補充:
如果dao類繼承了JdbcDaoSupport這個類,用註解可以的方式實現注入DataSource,代碼如下:

public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao {

    @Autowired
    @Qualifier("dataSource") //這裏也可以不按照名稱注入,按照類型注入也是可以的。
    public void setSuperDataSource(DataSource dataSource ) {
        super.setDataSource(dataSource);
    }
}

3.Spring中事務控制的API

(1)PlatformTransactionManager事務管理器

包含3個具體的操作:
獲取事務狀態信息

TransactionStatus getTransaction(TransactionDefinition definition)
提交事務

void commit(TransactionStatus status)
回滾事務

void rollback(TransactionStatus status)

開發中,使用它的實現類

org.springframework.jdbc.datasource.DataSourceTransactionManager 使用Spring JDBC或iBatis進行持久化數據時使用
org.springframework.orm.hibernate5.HibernateTransactionManager 使用Hibernate版本進行持久化數據時使用

(2)TransactionDefinition事務定義

①它是事務的定義信息對象,裏面有如下的方法:

獲取事務對象名稱:String getName()
獲取事務隔離級(共4個,默認使用數據庫隔離):int getIsolationLevel()
獲取事務傳播行爲(什麼情況下必須有事務:增刪改,什麼情況下可有可沒有:查詢):int getPropagationBehavior()
獲取事務超時事件(提交/回滾多久過期):int getTimeout()
獲取事務是否只讀:boolean isReadOnly()

②事務的隔離級別

反映事務提交併發訪問時的處理態度
ISOLATION DEFAULT:默認級別,歸屬於下列的某一種 (一般情況下使用這種配置既可)
ISOLATION_READ_UNCOMMITTED:可以讀取未提交數據 (最低的隔離級別,會產生髒讀,不可重複讀和幻像讀。)
ISOLATION_READ_COMMITTED:只能讀取已提交數據,解決髒讀問題(Oracle默認級別)
ISOLATION_REPEATABLE_READ:是否讀取其他事務提交修改後的數據,解決不可重複讀問題(MySql默認級別)
ISOLATION_SERIALIZABLE:是否讀取其他事務提交添加後的數據,解決幻讀問題 (花費最高代價但是最可靠 事務被處理爲順序執行)

③事務的傳播行爲

REQUIRED:如果當前沒有事務,就新建一個事務,如果已經存在一個事務中,加入到這個事務中。一般的選擇(默認值)
SUPPORTS:支持當前事務,如果當前沒有事務,就以非事務方式執行(沒有事務)
MANDATORY:使用當前的事務,如果當前沒有事務,就拋出異常
REQUERS_NEW:新建事務,如果當前在事務中,把當前事務掛起。
NOT_SUPPORTED:以非事務方式執行操作,如果當前存在事務,就把當前事務掛起
NEVER:以非事務方式運行,如果當前存在事務,拋出異常
NESTED:如果當前存在事務,則在嵌套事務內執行。如果當前沒有事務,則執行 REQUIRED 類似的操作。

④超時時間

默認值是-1,沒有超時限制,如果有,以秒爲單位進行設置

⑤是否是隻讀事務

建議查詢時設置只讀

(3)TransactionStatus事務狀態

存儲點:如果某一步沒有成功,回滾的時候只回滾到當前的步驟。
TransactionStatus接口描述了某個時間上事務對象的狀態信息,包含有6個具體的操作:
刷新事務:void flush()
獲取是否存在的存儲點:boolean hasSavepoint()
獲取事務是否完成:boolean isCompleted()
獲取事務是否爲新的事務:boolean isNewTransaction()
獲取事務是否回滾:boolean isRollbackOnly()
設置事務回滾:void setRollbackOnly()

4.Spring事務控制代碼

(1)事務控制

在這裏插入圖片描述

bean.xml

<!-- 配置業務層-->
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
    <property name="accountDao" ref="accountDao"></property>
</bean>


<!-- 配置賬戶的持久層-->
<bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
    <property name="dataSource" ref="dataSource"></property>
</bean>

<!-- 配置數據源-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
    <property name="url" value="jdbc:mysql://localhost:3306/eesy"></property>
    <property name="username" value="root"></property>
    <property name="password" value="1234"></property>
</bean>

(2)基於xml的聲明式事務控制

導入事務相關名稱空間

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

事務配置步驟

1、配置事務管理器
2、配置事務的通知
此時我們需要導入事務的約束 tx名稱空間和約束,同時也需要aop的
使用tx:advice標籤配置事務通知
屬性:
id:給事務通知起一個唯一標識
transaction-manager:給事務通知提供一個事務管理器引用
3、配置AOP中的通用切入點表達式
4、建立事務通知和切入點表達式的對應關係
5、配置事務的屬性
是在事務的通知tx:advice標籤的內部
bean.xml文件

<!-- 配置事務管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
</bean>


<!-- 配置事務的通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <!-- 配置事務的屬性
            isolation:用於指定事務的隔離級別。默認值是DEFAULT,表示使用數據庫的默認隔離級別。
            propagation:用於指定事務的傳播行爲。默認值是REQUIRED,表示一定會有事務,增刪改的選擇。查詢方法可以選擇SUPPORTS。
            read-only:用於指定事務是否只讀。只有查詢方法才能設置爲true。默認值是false,表示讀寫。
            timeout:用於指定事務的超時時間,默認值是-1,表示永不超時。如果指定了數值,以秒爲單位。
            rollback-for:用於指定一個異常,當產生該異常時,事務回滾,產生其他異常時,事務不回滾。沒有默認值。表示任何異常都回滾。
            no-rollback-for:用於指定一個異常,當產生該異常時,事務不回滾,產生其他異常時事務回滾。沒有默認值。表示任何異常都回滾。
    -->
    <tx:attributes>
        <tx:method name="*" propagation="REQUIRED" read-only="false"/>
        <tx:method name="find*" propagation="SUPPORTS" read-only="true"></tx:method>
    </tx:attributes>
</tx:advice>


<!-- 配置aop-->
<aop:config>
    <!-- 配置切入點表達式-->
    <aop:pointcut id="pt1" expression="execution(* com.itheima.service.impl.*.*(..))"></aop:pointcut>
    <!--建立切入點表達式和事務通知的對應關係 -->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"></aop:advisor>
</aop:config>

(3)基於註解的聲明式事務控制

spring中基於註解 的聲明式事務控制配置步驟
1、配置事務管理器
2、開啓spring對註解事務的支持
3、在需要事務支持的地方使用@Transactional註解

在bean.xml文件中加入context的相關名稱空間配置事務管理器

<!-- 配置事務管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
</bean>
開啓spring對註解事務的支持

<!-- 開啓spring對註解事務的支持-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

在需要事務支持的地方使用@Transactional註解

@Service("accountService")
@Transactional(propagation= Propagation.SUPPORTS,readOnly=true)//只讀型事務的配置
public class AccountServiceImpl implements IAccountService{

(4)純註解的聲明式事務控制

在這裏插入圖片描述

5.Spring編程式事務控制

在這裏插入圖片描述
這種方式耦合度比較高,並且產生大量的重複代碼,不建議採用

6.Spring5的新特性

(1)JDK版本要求

spring5.0 在 2017 年 9 月發佈了它的 GA(通用)版本。該版本是基於 jdk8 編寫的,所以 jdk8 以下版本將無法使用。同時,可以兼容 jdk9 版本。tomcat 版本要求 8.5 及以上。
注:
我們使用 jdk8 構建工程,可以降版編譯。但是不能使用 jdk8 以下版本構建工程。
由於 jdk 和 tomcat 版本的更新,我們的 IDE 也需要同時更新。(目前使用的 eclipse 4.7.2)

(2)利用JDK8更新的內容

核心容器的更新
JetBrains Kotlin 語言支持
響應式編程風格
Junit5 支持
依賴類庫的更新

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章