什么叫做切片。。什么叫做AOP。。。
與大多數技術一樣,AOP已經形成了自己的術語。描述切面的常用術語有通知(advice)、切點(pointcut)和連接點(join point)。
(一大串書上的原文!!)
通知(Advice)
在AOP術語中,切面的工作被稱為通知。
當抄表員出現在我們家門口時,他們要登記用電量並回去向電力公司報告。顯然,他們必須有一份需要抄表的住戶清單,他們所匯報的信息也很重要,但記錄用電量才是抄表員的主要工作。
類似地,切面也有目標——它必須要完成的工作。在AOP術語中,切面的工作被稱為通知。
通知定義了切面是什么以及何時使用。除了描述切面要完成的工作,通知還解決了何時執行這個工作的問題。它應該應用在某個方法被調用之前?之后?之前和之后都調用?還是只在方法拋出異常時調用?
Spring切面可以應用5種類型的通知:
- 前置通知(Before):在目標方法被調用之前調用通知功能;
- 后置通知(After):在目標方法完成之后調用通知,此時不會關心方法的輸出是什么;
- 返回通知(After-returning):在目標方法成功執行之后調用通知;
- 異常通知(After-throwing):在目標方法拋出異常后調用通知;
- 環繞通知(Around):通知包裹了被通知的方法,在被通知的方法調用之前和調用之后執行自定義的行為。
連接點(Join point)
電力公司為多個住戶提供服務,甚至可能是整個城市。每家都有一個電表,這些電表上的數字都需要讀取,因此每家都是抄表員的潛在目標。抄表員也許能夠讀取各種類型的設備,但是為了完成他的工作,他的目標應該房屋內所安裝的電表。
同樣,我們的應用可能也有數以千計的時機應用通知。這些時機被稱為連接點。連接點是在應用執行過程中能夠插入切面的一個點。這個點可以是調用方法時、拋出異常時、甚至修改一個字段時。切面代碼可以利用這些點插入到應用的正常流程之中,並添加新的行為。
切點(Poincut)
如果讓一位抄表員訪問電力公司所服務的所有住戶,那肯定是不現實的。實際上,電力公司為每一個抄表員都分別指定某一塊區域的住戶。類似地,一個切面並不需要通知應用的所有連接點。切點有助於縮小切面所通知的連接點的范圍。
如果說通知定義了切面的“什么”和“何時”的話,那么切點就定義了“何處”。切點的定義會匹配通知所要織入的一個或多個連接點。我們通常使用明確的類和方法名稱,或是利用正則表達式定義所匹配的類和方法名稱來指定這些切點。有些AOP框架允許我們創建動態的切點,可以根據運行時的決策(比如方法的參數值)來決定是否應用通知。
切面(Aspect)
當抄表員開始一天的工作時,他知道自己要做的事情(報告用電量)和從哪些房屋收集信息。因此,他知道要完成工作所需要的一切東西。
切面是通知和切點的結合。通知和切點共同定義了切面的全部內容——它是什么,在何時和何處完成其功能。
引入(Introduction)
引入允許我們向現有的類添加新方法或屬性。例如,我們可以創建一個Auditable通知類,該類記錄了對象最后一次修改時的狀態。這很簡單,只需一個方法,setLastModified(Date),和一個實例變量來保存這個狀態。然后,這個新方法和實例變量就可以被引入到現有的類中,從而可以在無需修改這些現有的類的情況下,讓它們具有新的行為和狀態。
織入(Weaving)
織入是把切面應用到目標對象並創建新的代理對象的過程。切面在指定的連接點被織入到目標對象中。在目標對象的生命周期里有多個點可以進行織入:
編譯期:切面在目標類編譯時被織入。這種方式需要特殊的編譯器。AspectJ的織入編譯器就是以這種方式織入切面的。
類加載期:切面在目標類加載到JVM時被織入。這種方式需要特殊的類加載器(ClassLoader),它可以在目標類被引入應用之前增強該目標類的字節碼。AspectJ5的加載時織入(load-time weaving,LTW)就支持以這種方式織入切面。
運行期:切面在應用運行的某個時刻被織入。一般情況下,在織入切面時,AOP容器會為目標對象動態地創建一個代理對象。Spring AOP就是以這種方式織入切面的。要掌握的新術語可真不少啊。再看一下圖4.1,現在我們已經了解了如下的知識,通知包含了需要用於多個應用對象的橫切行為;連接點是程序執行過程中能夠應用通知的所有點;切點定義了通知被應用的具體位置(在哪些連接點)。其中關鍵的概念是切點定義了哪些連接點會得到通知。
Spring借助AspectJ的切點表達式語言來定義Spring切面,對應參數如下:
- arg(): 限制連接點匹配參數為指定類型的執行方法
- @args(): 限制連接點匹配參數由指定注解標注的執行方法
- execution():用於匹配是連接點的執行方法
- this(): 限制連接點匹配AOP代理的bean引用為指定類型的類
- target: 限制連接點匹配目標對象為指定類型的類
- @target(): 限制連接點匹配特定的執行對象,這些對象對應的類要具有指定類型的注解
- within(): 限制連接點匹配指定的類型
- @within():限制連接點匹配指定注解所標注的類型(當使用Spring AOP時,方法定義在由指定的注解所標注的類里)
- @annotation: 限定匹配帶有指定注解的連接點
下面就讓我們來看如何編寫切點的。
首先定一個接口,Song 表示唱的歌。。
public interface Song { void song(); }
定義切片的格式如下:
下面就讓我們定一個切片; 觀看演唱會的切片,這邊定義的是com.aop.test1.Song中的song()方法 ,返回的類型為任意,方法參數也為任意。這里我通過@Pointcut來定一個重復的切片的,這邊一個一個定義也是可以的。
@Aspect public class VocalConcert { @Pointcut("execution(* com.aop.test1.Song.song(..))") public void doing() { } @Before("doing()") public void checking() { System.out.println("檢票之后,找位子坐下"); } @AfterReturning("doing()") public void beautiful() { System.out.println("演唱會進入精彩部分的時候,鼓掌!"); } @AfterReturning("doing()") public void leave() { System.out.println("結束后,我們離開場地"); } /* @Around("doing()") public void watchVocalConcert(ProceedingJoinPoint joinPoint) { try { System.out.println("檢票之后,找位子坐下"); joinPoint.proceed(); System.out.println("演唱會進入精彩部分的時候,鼓掌!"); System.out.println("結束后,我們離開場地"); } catch (Throwable throwable) { System.out.println("效果不好,退票"); } }*/ }
pring使用AspectJ注解來聲明通知方法,注解參數如下:
- @After: 通知方法會在目標方法返回或拋出異常后調用
- @AfterReturning: 通知方法會在目標方法返回后調用
- @AfterThrowing: 通知方法會在目標方法拋出異常后調用
- @Around: 通知方法會將目標方法封裝起來
- @Before: 通知方法會在目標方法調用之前執行
既然有歌曲的接口,就讓我們來創造個接口的實例類:
@Component public class ManMan implements Song { public void song() { System.out.println("下面這首歌是張學友的《慢慢》"); } }
java配置類
@Configuration @EnableAspectJAutoProxy @ComponentScan public class VocalConcertConfig { @Bean public VocalConcert vocalConcert() { return new VocalConcert(); } }
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:context="http://www.springframework.org/schema/context" 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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <context:component-scan base-package="com.aop.test1"/> <bean class="com.aop.test1.VocalConcert" id="concert"/> <aop:aspectj-autoproxy/> </beans>
然后我們測試下:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = VocalConcertConfig.class) public class VocalConcertTest { @Autowired Song mm; @Test public void log() { mm.song(); } }
測試結果:
至於環繞通知就是上面 被注釋的watchVocalConcert() 方法,這邊主要關注的是proceed()方法。
如果不調這個方法的話,那么你的通知實際上會阻塞對被通知方法的調用。有可能這就是你想要的效果,但更多的情況是你希望在某個點上執行被通知的方法。
有意思的是,你可以不調用proceed()方法,從而阻塞對被通知方法的訪問,與之類似,你也可以在通知中對它進行多次調用。要這樣做的一個場景就是實現重試邏輯,也就是在被通知方法失敗后,進行重復嘗試。
如果我們碰到所通知的方法有參數,那怎么辦呢?
我們只需要根據上面的實例定義就可以了。具體的例子就略過了。。、
上面就是簡單總結下Spring是什么,以及如何用注解創建簡單的切面。如有錯誤,請指出,謝謝
代碼:https://github.com/eoooxy/springinaction test下 的com.aop.test1 中