标簽: Java與設計模式
AOP(Aspect Oriented Programing)面向切面程式設計采用橫向抽取機制,以取代傳統的縱向繼承體系的重複性代碼(如性能監控/事務管理/安全檢查/緩存實作等).
橫向抽取代碼複用: 基于代理技術,在不修改原來代碼的前提下,對原有方法進行增強.
1.2開始, Spring開始支援AOP技術(Spring AOP)
Spring AOP使用純Java實作,不需要專門的編譯過程和類加載器,在運作期通過代理方式向目标類織入增強代碼.
2.0之後, 為了簡化AOP開發, Spring開始支援AspectJ(一個基于Java的AOP架構)架構.
術語
中文
描述
Joinpoint
連接配接點
指那些被攔截到的點.在Spring中,這些點指方法(因為Spring隻支援方法類型的連接配接點).
Pointcut
切入點
指需要(配置)被增強的Joinpoint.
Advice
通知/增強
指攔截到Joinpoint後要做的操作.通知分為前置通知/後置通知/異常通知/最終通知/環繞通知等.
Aspect
切面
切入點和通知的結合.
Target
目标對象
需要被代理(增強)的對象.
Proxy
代理對象
目标對象被AOP 織入 增強/通知後,産生的對象.
Weaving
織入
指把增強/通知應用到目标對象來建立代理對象的<code>過程</code>(Spring采用動态代理織入,AspectJ采用編譯期織入和類裝載期織入).
Introduction
引介
一種特殊通知,在不修改類代碼的前提下,可以在運作期為類動态地添加一些Method/Field(不常用).
cglib(Code Generation Library)是一個開源/高性能/高品質的Code生成類庫,可以在運作期動态擴充Java類與實作Java接口.
UserDAO(并沒有實作接口)
CGLibProxyFactory
Spring AOP的底層通過JDK/cglib動态代理為目标對象進行橫向織入:
1) 若目标對象實作了接口,則Spring使用JDK的<code>java.lang.reflect.Proxy</code>代理.
2) 若目标對象沒有實作接口,則Spring使用cglib庫生成目标對象的子類.
Spring隻支援方法連接配接點,不提供屬性連接配接.
标記為<code>final</code>的方法不能被代理,因為無法進行覆寫.
程式應優先對針對接口代理,這樣便于程式解耦/維護.
AOP聯盟為通知<code>Advice</code>定義了<code>org.aopalliance.aop.Advice</code>接口, Spring在<code>Advice</code>的基礎上,根據通知在目标方法的連接配接點位置,擴充為以下五類:
通知
接口
前置通知
<code>MethodBeforeAdvice</code>
在目标方法執行前實施增強
後置通知
<code>AfterReturningAdvice</code>
…執行後實施增強
環繞通知
<code>MethodInterceptor</code>
..執行前後實施增強
異常抛出通知
<code>ThrowsAdvice</code>
…抛出異常後實施增強
引介通知
<code>IntroductionInterceptor</code>
在目标類中添加新的方法和屬性(少用)
添加Spring的AOP依賴
使用Spring的AOP和AspectJ需要在pom.xml中添加如下依賴:
定義Target
定義Advice
配置代理
Spring最原始的AOP支援, 手動指定目标對象與通知(沒有使用AOP名稱空間).
Client
這種方式的缺陷在于每個<code>Target</code>都必須手動指定<code>ProxyFactoryBean</code>對其代理(不能批量指定),而且這種方式會在Spring容器中存在兩份Target對象(代理前/代理後),浪費資源,且容易出錯(比如沒有指定<code>@Qualifier</code>).
通過AspectJ引入Pointcut切點定義
Target/Advice同前
定義切面表達式
通過execution函數定義切點表達式(定義切點的方法切入) <code>execution(<通路修飾符> <傳回類型><方法名>(<參數>)<異常>)</code> 如: 1) <code>execution(public * *(..))</code> # 比對所有<code>public</code>方法. 2) <code>execution(* com.fq.dao.*(..))</code> # 比對指定包下所有類方法(不包含子包) 3) <code>execution(* com.fq.dao..*(..))</code> # 比對指定包下所有類方法(包含子包) 4) <code>execution(* com.fq.service.impl.OrderServiceImple.*(..))</code> # 比對指定類所有方法 5) <code>execution(* com.fq.service.OrderService+.*(..))</code> # 比對實作特定接口所有類方法 6) <code>execution(* save*(..))</code> # 比對所有save開頭的方法
Client同前
AspectJ是一個基于Java的AOP架構,提供了強大的AOP功能,其他很多AOP架構都借鑒或采納了AspectJ的一些思想,Spring2.0以後增加了對AspectJ切點表達式支援(如上),并在Spring3.0之後與AspectJ進行了很好的內建.
在Java領域,AspectJ中的很多文法結構基本上已成為AOP領域的标準, 他定義了如下幾類通知類型:
<code>@Before</code>
相當于<code>BeforeAdvice</code>
<code>@AfterReturning</code>
相當于<code>AfterReturningAdvice</code>
<code>@Around</code>
相當于<code>MethodInterceptor</code>
抛出通知
<code>@AfterThrowing</code>
相當于<code>ThrowAdvice</code>
<code>@DeclareParents</code>
相當于<code>IntroductionInterceptor</code>
最終final通知
<code>@After</code>
不管是否異常,該通知都會執行
新版本Spring,建議使用AspectJ方式開發以簡化AOP配置.
使用AspectJ編寫Advice無需實作任何接口,而且可以将多個通知寫入一個切面類.
定義通知
裝配
前置通知小結
前置通知會保證在目标方法執行前執行;
前置通知預設不能阻止目标方法執行(但如果通知抛出異常,則目标方法無法執行);
可以通過<code>JoinPoint</code>參數獲得目前攔截對象和方法等資訊.
後置通知可以獲得方法傳回值,但在配置檔案定義傳回值參數名必須與後置通知方法參數名一緻(如<code>result</code>).
環繞通知可以實作任何通知的效果, 甚至可以阻止目标方法的執行.
<code>throwing</code>屬性指定異常對象名, 該名稱應和方法定義參數名一緻.
無論目标方法是否出現異常,該通知都會執行(類似<code>finally</code>代碼塊, 應用場景為釋放資源).
<code>@AspectJ</code>是AspectJ 1.5新增功能,可以通過JDK注解技術,直接在Bean類中定義切面.
AspectJ預定義的注解有:<code>@Before</code>/<code>@AfterReturning</code>/<code>@Around</code>/<code>@AfterThrowing</code>/<code>@DeclareParents</code>/<code>@After</code>.描述同前.
使用AspectJ注解AOP需要在applicationContext.xml檔案中開啟注解自動代理功能:
<code>OrderService</code>/<code>Client</code>同前
如果不調用<code>ProceedingJoinPoint</code>的<code>proceed</code>方法,那麼目标方法就不執行了.
對于重複的切點,可以使用<code>@Pointcut</code>進行定義, 然後在通知注解内引用.
定義切點方法
無參/無傳回值/方法名為切點名:
引用切點
在Advice上像調用方法一樣引用切點:
1) 如果切點與切面在同一個類内, 可省去類名字首; 2) 當需要通知多個切點時,可以使用<code>||</code>/<code>&&</code>進行連接配接.
權限控制(少用)
少用
權限控制/性能監控/緩存實作/事務管理
異常通知
發生異常後,記錄錯誤日志
最終通知
釋放資源