1. 程式人生 > >Spring基礎-AOP基本概念和特點(四)

Spring基礎-AOP基本概念和特點(四)

AOP(Aspect Oriented Programming)

面向切面程式設計,通過預編譯方式和執行期動態代理實現程式功能的統一維護的一種技術。

  • 主要的功能是:日誌記錄、效能統計、安全控制、事務處理、異常處理等等

AOP實現方式

  • 預編譯

    • AspectJ
  • 執行期動態代理(JDK動態代理、CGLib動態代理)

    • SpringAOP、JbossAOP

AOP的相關概念

  • 切面(Aspect):一個關注點的模組化,這個關注點可能會橫切多個物件
  • 連線點(Joinpoint):程式執行過程中的某個特定的點
  • 通知(Advice):在切面的某個特定的連線點上執行的動作
  • 切入點(Pointcut):匹配連線點的斷言,在AOP中通知和一個切入點表示式關聯
  • 引入(Introduction):在不修改程式碼的前提下,為類新增新的方法和屬性
  • 目標物件(Target Object):被一個或者多個切面所通知的物件
  • AOP代理(AOP):AOP框架建立的物件,用來實現切面契約(aspect contract)(包括通知方法執行等功能)
  • 織入(Weaving):把切面連線到其他的應用程式型別或者物件上,並建立一個被通知的物件,分為:編譯時織入、類載入時織入、執行時織入

Advice的型別

  • 前置通知(Before advice):在某連線點(join point)之前執行的通知,但不能阻止連線點前的執行(除非它丟擲一個異常)
  • 返回後通知(After returning advice):在某連線點(join point)正常完成後執行的通知
  • 丟擲異常後通知(After throwing advice):在方法丟擲異常退出時執行的通知
  • 後通知(After(finally) advice):當某連線點退出的時候執行的通知(不論是正常返回還是異常返回)
  • 環繞通知(Around Advice):包圍一個連線點(join point)的通知

Spring框架中AOP的用途

  • 提供了宣告式的企業服務,特別是EJB的替代服務的宣告
  • 允許使用者定製自己的方面,以完成OOP與AOP的互補使用

Spring的AOP實現

  • 純Java實現,無需特殊的編譯過程,不需要控制類載入器層次
  • 目前只支援方法執行連線點(通知Spring Bean的方法執行)
  • 不是為了提供最完整的AOP實現(儘管它非常強大);而是側重於提供一種AOP實現和Spring IOC容器之間的整合,用於幫助解決企業應用中的常見問題
  • Spring AOP不會與AspectJ競爭,從而提供綜合全面的AOP解決方案

有介面和無介面的Spring AOP實現區別

  • Spring AOP預設使用標準的JavaSE動態代理作為AOP代理,這使得任何介面(或者介面集)都可以被代理
  • Spring AOP中也可以使用CGLIB代理(如果一個業務物件並沒有實現一個介面)

基於Schema-based的AOP實現(基於配置的AOP實現)

Spring所有的切面和通知器都必須放在一個內(可以陪著包含多個元素),每一個可以包含pointcut、advisor和aspect元素(它們必須按照這個順序進行宣告)。風格的配置大量使用了Spring的自動代理機制。

  1. aspect
<bean id="moocAspect" class="com.imooc.aop.schema.advice.MoocAspect"></bean>

<bean id="aspectBiz" class="com.imooc.aop.schema.advice.biz.AspectBiz"></bean>

<aop:config>
    <aop:aspect id="moocAspectAOP" ref="moocAspect">
    </aop:aspect>
</aop:config>
  1. pointcut
    • execution(public **(..)):切入點為執行所有public方法
    • execution(* set *(..)):切入點為執行所有set開始的方法
    • execution(* com.xyz.service.AccountService.*(..)):切入點為執行AccountService類中的所有方法時
    • execution(* com.xyz.service..(..)):切入點為執行com.xyz.service包下的所有方法時
    • execution(* com.xyz.service…(..)):切入點為執行com.xyz.service包及其子包下的所有方法時
    • within(com.xyz.service.*):(only in Spring AOP)
    • within(com.xyz.service..*):(only in Spring AOP):withiin用於匹配指定型別內的方法執行
    • this(com.xyz.service.AccountService)(only in Spring AOP):this用於匹配當前AOP代理物件型別的執行方法
    • target(com.xyz.service.AccountService)(only iin Spring AOP):target用於匹配當前目標物件型別的執行方法
    • args(java.io.Serializable)(only in Spring AOP):args用於匹配當前執行的方法傳入的引數為指定型別的執行方法
    • @target(org.springframework.transaction.annotion.Transsactional)(only in Spring AOP)
    • @within(org.springframework.transaction.annotion.Transsactional)(only in Spring AOP)
    • @annotation(org.springframework.transaction.annotion.Transsactional)(only in Spring AOP)
    • @args(com.xyz.security.Classified)(only in Spring AOP)
    • bean(tradeService)(only in Spring AOP)
    • bean(*Service)(only in Spring AOP)
<aop:config>
    <aop:aspect id="moocAspectAOP" ref="moocAspect">
        <aop:pointcut id="moocPiontcut" 
            expression="execution(* com.imooc.aop.schema.advice.biz.*Biz.*(..))" />
    </aop:aspect>
</aop:config>
  1. advice

    • Before advice
<aop:config>
    <aop:aspect id="moocAspectAOP" ref="moocAspect">
        <aop:before pointcut="execution(* com.imooc.aop.schema.advice.biz.*Biz.*(..))" />
    </aop:aspect>
</aop:config>

或者

<aop:config>
    <aop:aspect id="moocAspectAOP" ref="moocAspect">
        <aop:before method="before" pointcut-ref="moocPiontcut"/>
    </aop:aspect>
</aop:config>
  1. After returning advice
<aop:config>
    <aop:aspect id="moocAspectAOP" ref="moocAspect">
        <aop:after-returning method="afterReturning" pointcut-ref="moocPiontcut"/>
    </aop:aspect>
</aop:config>

或者

<aop:config>
    <aop:aspect id="moocAspectAOP" ref="moocAspect">
        <aop:after-returning 
            method="doAccessCheck" 
            pointcut-ref="dataAccessOperation"
            returning="retVal"/>
    </aop:aspect>
</aop:config>
  1. After throwing advice
<aop:config>
    <aop:aspect id="moocAspectAOP" ref="moocAspect">
        <aop:after-throwing method="afterThrowing" pointcut-ref="moocPiontcut"/>
    </aop:aspect>
</aop:config>

使用throwing屬性來指定可被傳遞的異常的引數名稱

<aop:config>
    <aop:aspect id="moocAspectAOP" ref="moocAspect">
        <aop:after-throwing 
        method="doRecoveryActions" 
        pointcut-ref="dataAccessOperation"
        throwing="dataAccessEx"/>
    </aop:aspect>
</aop:config>
  1. After (finally) advice
<aop:config>
    <aop:aspect id="moocAspectAOP" ref="moocAspect">
        <aop:after method="after" pointcut-ref="moocPiontcut"/>
    </aop:aspect>
</aop:config>
  1. around advice
    -通知方法的第一個引數必須是ProceedingJoinPoing型別
<aop:config>
    <aop:aspect id="moocAspectAOP" ref="moocAspect">
        <aop:around method="around" pointcut-ref="moocPiontcut"/>
    </aop:aspect>
</aop:config>

advice parameter

<aop:config>
    <aop:aspect id="moocAspectAOP" ref="moocAspect">
       <aop:around method="aroundInit" pointcut="execution(* com.imooc.aop.schema.advice.biz.AspectBiz.init(String, int)) and args(bizName, times)"/>
    </aop:aspect>
</aop:config>
  1. Introductions

    • 簡介允許一個切面宣告一個實現指定介面的通知物件,並且提供了一個介面實現類來代表這些物件
    • 由中的元素宣告該元素用於宣告所匹配的型別擁有一個新的parent(因此得名)
<aop:config>
    <aop:aspect id="moocAspectAOP" ref="moocAspect">
       <aop:declare-parents types-matching="com.imooc.aop.schema.advice.biz.*(+)" 
        implement-interface="com.imooc.aop.schema.advice.Fit"
        default-impl="com.imooc.aop.schema.advice.FitImpl"/>
    </aop:aspect>
</aop:config>
  1. Advisors
    • advisor就像一個小的自包含的方面,只有一個advice
    • 切面自身通過一個bean表示,並且必須實現某個advice介面,同時,advisor也可以很好的利用AspectJ切入點表示式
    • Spring通過配置檔案中元素支援advisor,實際使用中,大多數情況下它會和transactional advice配合使用
    • 為了定義一個advisor的優先順序以便讓advice可以有序,可以使用order屬性來定義advisor的順序。
<context:component-scan base-package="com.imooc.aop.schema"></context:component-scan>

<aop:config>
    <aop:aspect id="concurrentOperationRetry" ref="concurrentOperationExecutor">
        <aop:pointcut id="idempotentOperation"
            expression="execution(* com.imooc.aop.schema.advisors.service.*.*(..)) " />
<!--                expression="execution(* com.imooc.aop.schema.service.*.*(..)) and -->
<!--                                @annotation(com.imooc.aop.schema.Idempotent)" /> -->
        <aop:around pointcut-ref="idempotentOperation" method="doConcurrentOperation" />
    </aop:aspect>
</aop:config>

<bean id="concurrentOperationExecutor" class="com.imooc.aop.schema.advisors.ConcurrentOperationExecutor">
    <property name="maxRetries" value="3" />
    <property name="order" value="100" />
</bean>