Java框架之Spring 02-AOP-動態代理-AspectJ-JdbcTemplate-事務

AOP

動態代理

  代理設計模式的原理:使用一個代理將本來對象包裝起來,而後用該代理對象」取代」原始對象。任何對原始對象的調用都要經過代理。代理對象決定是否以及什麼時候將方法調用轉到原始對象上。java

代理模式的三要素:mysql

  • 代理主題接口程序員

  • 代理者spring

  • 被代理者sql

代理模式的主要優勢數據庫

  • 代理模式在客戶端與目標對象之間起到一箇中介做用和保護目標對象的做用;express

  • 代理對象能夠擴展目標對象的功能;編程

  • 代理模式能將客戶端與目標對象分離,在必定程度上下降了系統的耦合度;後端

其主要缺點設計模式

  • 在客戶端和目標對象之間增長一個代理對象,會形成請求處理速度變慢;

  • 增長了系統的複雜度;

動態代理的方式

   靜態代理類只能替一個主題接口進行代理工做

       基於接口實現動態代理: JDK動態代理

       基於繼承實現動態代理: Cglib、Javassist動態代理

JDK動態代理步驟:
* 一、編寫主題接口
* 二、編寫被代理類
* 三、編寫代理工做處理器:即代理類要替被代理類作什麼事情(有參構造器)
* 要求:必須實現InvocationHandler,重寫
* Object invoke(Object proxy, Method method, Object[] args)
* 第一個參數:代理類對象
* 第二個參數:被代理類和代理類 要執行的方法
* 第三個參數:要執行方法的實參列表
* 這個invoke方法不是程序員調用,當代理類對象執行對應的代理方法時,自動調用的
* 四、建立代理類及其對象
* 須要:Proxy:提供用於建立動態代理類和實例的靜態方法,它仍是由這些方法建立的全部動態代理類的超類。
* static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
* 第一個參數:被代理類的類加載器,咱們但願被代理和代理類使用同一個類加載器
* 第二個參數:被代理類實現的接口們
* 第三個參數:代理工做處理器對象
* 五、調用被代理的方法

  注意:代理對象和實現類對象,都實現了相同的接口。屬於兄弟關係。(不能強制轉換爲,實現類對象)

AOP概述

  1) AOP(Aspect-Oriented Programming,面向切面編程):是一種新的方法論,是對傳統 OOP(ObjectOrientedProgramming),面向對象編程)的補充。

  面向對象  縱向繼承機制

  面向切面  橫向抽取機制

  2) AOP編程操做的主要對象是切面(aspect),而切面用於橫切關注點。
  3) 在應用AOP編程時,仍然須要定義公共功能,但能夠明確的定義這個功能應用在哪裏,以什麼方式應用,而且沒必要修改受影響的類。這樣一來橫切關注點就被模塊化到特殊的類裏——這樣的類咱們一般稱之爲「切面」。

    4) AOP的好處

    ① 每一個事物邏輯位於一個位置,代碼不分散,便於維護和升級

    ② 業務模塊更簡潔,只包含核心業務代碼

AOP術語

1.橫切關注點
  從每一個方法中抽取出來的同一類非核心業務。
2.切面(Aspect)
  封裝橫切關注點信息的類,每一個關注點體現爲一個通知方法。
3.通知(Advice)
  切面必需要完成的各個具體工做
4.目標(Target)
  被通知的對象
5.代理(Proxy)
  向目標對象應用通知以後建立的代理對象
6. 鏈接點(Joinpoint)
  橫切關注點在程序代碼中的具體體現,對應程序執行的某個特定位置。例如:類某個方法調用前、調用後、方法捕獲到異常後等。
7. 切入點(pointcut):

  定位鏈接點的方式。每一個類的方法中都包含多個鏈接點,因此鏈接點是類中客觀存在的事物。
  若是把鏈接點看做數據庫中的記錄,那麼切入點就是查詢條件——AOP能夠經過切入點定位到特定的鏈接點。
  切點經過org.springframework.aop.Pointcut 接口進行描述,它使用類和方法做爲鏈接點的查詢條件。

AspectJ

 啓用AspectJ註解支持

一、導入JAR包

二、引入aop名稱空間

三、配置:<aop:aspectj-autoproxy> 

  當Spring IOC容器偵測到bean配置文件中的<aop:aspectj-autoproxy>元素時,會自動爲與AspectJ切面匹配的bean建立代理

 用AspectJ註解聲明切面

     在Spring中聲明AspectJ切面爲bean實例

  初始化AspectJ切面以後,容器就會爲那些與 AspectJ切面相匹配的bean建立代理

  在AspectJ註解中,切面只是一個帶有@Aspect註解的Java類

  通知是標註有某種註解的Java方法

  5種類型的通知註解:@Before(value="切入點表達式")

    ① @Before:前置通知,在方法執行以前執行

    ② @After:後置通知,在方法執行以後執行,即不管鏈接點是正常返回仍是拋出異常,後置通知都會執行

    ③ @AfterRunning:返回通知,在方法返回結果以後執行   (若是異常,不執行 )

      在返回通知中訪問鏈接點的返回值,若是隻想在鏈接點返回的時候記錄日誌,應使用返回通知代替後置通知

      ①在返回通知中,只要將returning屬性添加到@AfterReturning註解中,就能夠訪問鏈接點的返回值。

      ②必須在通知方法的簽名中添加一個同名參數。在運行時Spring AOP會經過這個參數傳遞返回值

      ③原始的切點表達式須要出如今pointcut屬性中

    ④ @AfterThrowing:異常通知,在方法拋出異常以後執行  (若是無異常,不執行)

      將throwing屬性添加到@AfterThrowing註解中,在異常通知方法能夠捕獲到任何錯誤和異常。

      也能夠將參數聲明爲其餘異常的參數類型。而後通知就只在拋出這個類型及其子類的異常時才被執行

    ⑤ @Around:環繞通知,圍繞着方法執行

      可以全面地控制鏈接點,甚至能夠控制是否執行鏈接點。

      鏈接點的參數類型必須是ProceedingJoinPoint。它是 JoinPoint的子接口,容許控制什麼時候執行,是否執行鏈接點。

      須要明確調用ProceedingJoinPoint的proceed()方法來執行被代理的方法。

      注意:環繞通知的方法須要返回目標方法執行以後的結果,即調用 joinPoint.proceed();的返回值,不然會出現空指針異常

    @Around(value = "rePointCut()")
    public Object aroundMethod(ProceedingJoinPoint pjp) {
        Object obj = null;
        try {
            //前置通知
            System.out.println("前置通知");
            obj = pjp.proceed();    //調用目標對象的方法
            //返回通知
            System.out.println("返回通知,結果:" + obj);
        } catch (Throwable e) {
            //異常通知
            System.out.println("異常通知,ex:" + e);
            e.printStackTrace();
        } finally {
            //後置通知
            System.out.println("後置通知");
        }
        return obj;
    }

 

切入點表達式

經過表達式的方式定位一個或多個具體的鏈接點。

語法格式

execution([權限修飾符] [返回值類型] [簡單類名/全類名] [方法名]([參數列表]))

 

表達式:    @Pointcut(value="execution(* com.spring.*.*(..))")
含義:       ArithmeticCalculator接口中聲明的全部方法。
    第一個「*」表明任意修飾符及任意返回值。
   第二個「*」表明,任意類的全類名稱|任意類名 第三個「
*」表明任意方法。 「..」匹配任意數量、任意類型的參數。 若目標類、接口與該切面類在同一個包中能夠省略包名。

 

切入點表達式能夠經過 「&&」、「||」、「!」等操做符結合起來。

重用切入點

  在AspectJ切面中,能夠經過@Pointcut註解將一個切入點聲明成簡單的方法。切入點的方法體一般是空的

  切入點方法的訪問控制符同時也控制着這個切入點的可見性。

  在引入這個切入點時,必須將類名也包括在內。若是類沒有與這個切面放在同一個包中,還必須包含包名。

  其餘通知能夠經過方法名稱引入該切入點

//提取表達式
@Pointcut(value="execution(* com.spring.aspectj.*.*(..))")
public void rePointCut() {}
@Before(value="rePointCut()"):當前類中重用切入點表達式

 

指定切面的優先級

在同一個鏈接點上應用不止一個切面時,除非明確指定,不然它們的優先級是不肯定的

使用@Order註解,序號出如今註解中

 

@Aspect
@Order(0) //int類型,數值越小,優先級越高。     
public class TestAspect{}

 

XML方式配置切面

  基於註解的聲明要優先於基於XML的聲明,經過AspectJ註解,切面能夠與AspectJ兼容,而基於XML的配置則是Spring專有的,因此不推薦

  在bean配置文件中,全部的Spring AOP配置都必須定義在<aop:config>元素內部。對於每一個切面而言,都要建立一個<aop:aspect>元素來爲具體的切面實現引用後端bean實例。

  切面bean必須有一個標識符,供<aop:aspect>元素引用。

1)聲明切入點

    切入點使用<aop:pointcut>元素聲明。

     ① 定義在<aop:aspect>元素下:只對當前切面有效

         ② 定義在<aop:config>元素下:對全部切面都有效

  基於XML的AOP配置不容許在切入點表達式中用名稱引用其餘切入點

2)聲明通知

  通知元素須要使用<pointcut-ref>來引用切入點

     method屬性指定切面類中通知方法的名稱

    <aop:config>
        <aop:pointcut id="myPointcut" expression="execution(* com.spring.aspectj_xml.*.*(..))" />
        <!-- 定義日誌切面 -->
        <aop:aspect id="loggingAspect" ref="loggingAspect" order="1">
            <aop:before method="beforeMethod" pointcut-ref="myPointcut"/>
            <aop:after method="afterMethod" pointcut-ref="myPointcut"/>
            <aop:after-returning method="afterReturnMethod" returning="rs" pointcut-ref="myPointcut"/>
            <aop:after-throwing method="afterThrowingMethod" throwing="ex" pointcut-ref="myPointcut"/>
        </aop:aspect>
    </aop:config>

 

JdbcTemplate

  能夠將Spring的JdbcTemplate看做是一個小型的輕量級持久化層框架,JdbcTemplate類是線程安全的

  JdbcTemplate所須要的JAR包

    spring-jdbc-4.0.0.RELEASE.jar

    spring-orm-4.0.0.RELEASE.jar

    spring-tx-4.0.0.RELEASE.jar

   數據庫驅動和數據源

    druid-1.1.9.jar

             mysql-connector-java-5.1.7-bin.jar

配置文件中配置相關的bean

    <!--    引入jdbc配置文件-->
        <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
    <!--    裝配Druid數據源conn-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
          p:url="${jdbc.url}"
          p:username="${jdbc.username}"
          p:password="${jdbc.password}"
          p:driverClassName="${jdbc.driverClass}"
    ></bean>
    <!--    經過數據源裝配JdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--    經過數據源裝配事務管理器-->
    <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--    啓用事務管理器-->
    <tx:annotation-driven transaction-manager="dataSourceTransactionManager"></tx:annotation-driven>

 

持久化操做

  1) 增刪改

    update(String sql,Object... args)

  2) 批處理增刪改

    batchUpdate(String sql,List<Object[]> batchArgs)  

    Object[]封裝了SQL語句每一次執行時所須要的參數

               List集合封裝了SQL語句屢次執行時的全部參數

    3)獲取單個數值型

    queryForObject(String sql,Class<T> requiredType,Object... args)

  4)獲取單個對象類型

    queryForObject(String sql,RowMapper rowMapper,Object... args)

  5)獲取多個JavaBean類型

    query(String sql,RowMapper rowMapper,Object... args)

RowMapper對象可使用BeanPropertyRowMapper實現類:注意new對象時指定類型

事務管理

   事務就是一組因爲邏輯上緊密關聯而合併成一個總體(工做單元)的多個數據庫操做,這些操做要麼都執行要麼都不執行

   事務的四個屬性(ACID)

     ①原子性(atomicity):「原子」的本意是「不可再分」,事務的原子性表現爲一個事務中涉及到的多個操做在邏輯上缺一不可。事務的原子性要求事務中的全部操做要麼都執行,要麼都不執行。

     ②一致性(consistency):「一致」指的是數據的一致,具體是指:全部數據都處於知足業務規則的一致性狀態

     ③隔離性(isolation):隔離性原則要求多個事務在併發執行過程當中不會互相干擾

     ④持久性(durability):一般狀況下,事務對數據的修改應該被寫入到持久化存儲器中。

編程式事務

使用原生的JDBC API實現事務管理是全部事務管理方式的基石,可是須要將事務管理代碼嵌入到業務方法中,事務與業務代碼相耦合,代碼相對分散且混亂。因此:建議使用聲明式事務。

         ①獲取數據庫鏈接Connection對象

         ②取消事務的自動提交

         ③執行操做

         ④正常完成操做時手動提交事務

         ⑤執行失敗時回滾事務

         ⑥關閉相關資源

聲明式事務

  事務管理代碼的固定模式做爲一種橫切關注點,能夠經過AOP方法模塊化,進而藉助Spring AOP框架實現聲明式事務管理。它將事務管理代碼從業務方法中分離出來

  Spring的核心事務管理抽象是它爲事務管理封裝了一組獨立於技術的方法。不管使用Spring的哪一種事務管理策略(編程式或聲明式),事務管理器都是必須的。開發人員能夠經過配置的方式進行事務管理。

         事務管理器能夠以普通的bean的形式聲明在Spring IOC容器中。

事務管理器的主要實現

  1) DataSourceTransactionManager:在應用程序中只須要處理一個數據源,並且經過JDBC存取。

  2) JtaTransactionManager:在JavaEE應用服務器上用JTA(Java Transaction API)進行事務管理

  3) HibernateTransactionManager:用Hibernate框架存取數據庫

實現

  1) 配置文件:如上圖

  2) 在須要進行事務控制的方法上加註解 @Transactional

    @Transactional(propagation=Propagation.REQUIRES_NEW,
            isolation=Isolation.READ_COMMITTED,
            timeout=3,
            readOnly=false,
            noRollbackFor=RuntimeException.class)
    public void purchase(String username, String isbn) {}

 

propagation屬性詳解

事務的傳播行爲

  當事務方法被另外一個事務方法調用時,必須指定事務應該如何傳播。事務的傳播行爲能夠由傳播屬性指定。Spring定義了7種類傳播行爲。

  事務傳播屬性經過在@Transactional註解的propagation屬性中定義。

  ①REQUIRED傳播行爲

    當一個事務方法調用另外一個事務方法時,它默認會在現有的事務內運行。所以在整個事務方法的開始和終止邊界內只有一個事務。即:若是當前存在事務,就使用當前事務。若是當前沒事務,就建立一個新的事務,去使用。

  ②. REQUIRES_NEW傳播行爲

    表示該事務方法必須啓動一個新事務,並在本身的事務內運行。若是有事務在運行,就應該先掛起它。即:不管當前是否存在事務,都必須建立新事務,去使用。等新建事務運行結束後,繼續執行被掛起事務

事務的隔離級別

  • 一個事務與其餘事務隔離的程度稱爲隔離級別。數據庫規定了多種事務隔離級別, 不一樣隔離級別對應不一樣的干擾程度, 隔離級別越高, 數據一致性就越好, 但併發性越弱。主要爲避免各類併發問題。

  • 數據庫提供的 4 種事務隔離級別:

  讀未提交(1),存在問題:髒讀
  讀已提交(2),存在問題:不可重複讀(建議使用)
  可重複讀(4),存在問題:幻讀(建議使用)
  串行化 (8),存在問題:效率低  

  用@Transactional註解聲明式地管理事務時能夠在@Transactional的isolation屬性中設置隔離級別

觸發事務回滾的異常

   捕獲到RuntimeException或Error時回滾,而捕獲到編譯時異常不回滾。

       經過@Transactional 註解

         ① rollbackFor屬性:指定遇到時必須進行回滾的異常類型,能夠爲多個

         ② noRollbackFor屬性:指定遇到時不回滾的異常類型,能夠爲多個

事務的超時和只讀屬性

   超時事務屬性:事務在強制回滾以前能夠保持多久。這樣能夠防止長期運行的事務佔用資源。

       只讀事務屬性: 表示這個事務只讀取數據但不更新數據, 這樣能夠幫助數據庫引擎優化事務。

  readOnly=true,
    true:事務只讀,一旦設置只讀屬性,該事務就不能進行:增刪改操做。
    false:設置事務爲,不僅讀。
  timeout=3,  設置事務的「強制回滾」時間秒。 

基於xml方式配置聲明式事務

    <!-- 配置事務切面 -->
    <aop:config>
        <aop:pointcut expression="execution(* com.tx.component.service.BookShopServiceImpl.purchase(..))"
                      id="txPointCut"/>
        <!-- 將切入點表達式和事務屬性配置關聯到一塊兒 -->
        <aop:advisor advice-ref="myTx" pointcut-ref="txPointCut"/>
    </aop:config>
    <!-- 配置基於XML的聲明式事務  -->
    <tx:advice id="myTx" transaction-manager="transactionManager">
        <tx:attributes>
            <!-- 設置具體方法的事務屬性 -->
            <tx:method name="find*" read-only="true"/>
            <tx:method name="get*" read-only="true"/>
            <tx:method name="purchase"
                       isolation="READ_COMMITTED"
                       no-rollback-for="java.lang.ArithmeticException,java.lang.NullPointerException"
                       propagation="REQUIRES_NEW"
                       read-only="false"
                       timeout="10"/>
        </tx:attributes>
    </tx:advice>
相關文章
相關標籤/搜索