spring學習(二) ———— AOP之AspectJ框架的使用
前面講解了spring的特性之一,IOC(控制反轉),因為有了IOC,所以我們都不需要自己new物件了,想要什麼,spring就給什麼。而今天要學習spring的第二個重點,AOP。一篇講解不完,所以這篇文章主要介紹一下什麼是AOP,如何去理解AOP。理解完之後,在spring中如何使用AspectJ AOP框架的。看得懂,寫的出spring配置的那麼就學的差不多了。加油。建議都自己手動實現一遍,這樣才能更好的理解。
--WH
一、什麼是AOP?
AOP:面向切面程式設計,採用橫向抽取機制,取代了傳統的縱向繼承
IOC:控制反轉
跟IOC一樣,我也不知道,這麼高大上的名稱,被嚇壞了,MD,但是通過前面一節的學習,IOC不就是讓spring給我們new物件嗎,而不需要我們自己建立,
而AOP是一種面向切面的思想,但是我們平常說的spring使用了AOP,實際上說的是spring實現AOP思想的底層原理,而底層原理就是使用動態代理來增強某個方法。所以平常說AOP技術實際上就是指通過動態代理來對方法進行增強。
比如:我們需要對一個已經寫好的類中的方法進行增強,在不改動該類方法的程式碼的情況下,如何做呢?
傳統縱向繼承
編寫一個類,繼承該類,重寫該類中的這種需要增強的方法,這樣確實可以達到我們的目的,但是一旦需要修改的方法所屬的類不是一個類,那麼就需要在多寫很多子類,增加很多方法,程式設計量就是一個很大的問題,並且後期要修改,工作量也很大。
橫向抽取機制,將要增強所用到的程式碼提取到一個類中,然後對需要增強的方法通過代理類去將其增強即可。
二、動態代理的兩個方式
JDK動態代理。介面+實現類
cglib位元組碼增強。 實現類
為了更好的理解spring的AOP技術,我們應該手動編寫以上兩種實現動態代理的方法,然後才能體會到spring實現AOP技術所帶來的便利。
2.1、JDK動態代理。
要使用JDK動態代理的類,必須要有介面。這是前提條件。
編寫UserService介面和UserServiceImpl。
建立代理類的工廠proxyFactory
增強程式碼的類
測試testApp
測試結果 成功增強了userServiceImpl物件的add方法。如果別的類有需要被增強的方法,那麼同樣通過建立工廠代理就可以拿到對應的代理物件。然後進行加強
2.2、cglib動態代理。
被代理的物件不需要在實現介面,要求就放鬆了很多。很多時候都採用cglib來進行動態代理。
需要匯入兩個jar包
spring已經將cglib和asm整合到了spring-core-3.2.0.jar中,所以我們匯入spring的jar包就不需要在重複導這兩個包了。
跟JDK動態代理的編寫流程是一樣的
UserServiceImpl。沒有介面了
增強方法的類
建立代理的工廠(和jdk動態代理有區別)
測試
成功增強方法。
注意:CGLib採用了非常底層的位元組碼技術,其原理是通過位元組碼技術為一個類建立子類,並在子類中採用方法攔截的技術攔截所有父類方法的呼叫,順勢織入橫切邏輯,所以 說被代理的類不能有final關鍵字
三、spring中使用AOP的相關術語
3.1、Target:目標類,需要被增強的類,也就是上面我們寫的UserServiceImpl。
3.2、JointPoint:連線點,目標類上需要被增強的方法,(這些方法可以被增強,也可以不增強,也就是說目標類中所有的方法都可以稱為是連線點)
3.3、PointCut:切入點,被增強的方法(已經確定這個方法要被增強),切入點就是一個連線點的子集
3.4、Advice:增強/通知,增強的程式碼,也就是上面將增強的程式碼湊成的一個類。類中的每個方法都代表一個增強的功能程式碼,這個類中的方法就被稱為通知
3.5、weaving:織入,將切入點和通知結合,從沒被增強到已經增強的過程
3.6、Aspect:切面,切入點和通知結合,切入點 和 通知點 多點形成面 特殊情況:一個切入點 和 一個通知
畫張圖就可以理解了。
3.7、Introduction(引介) 特殊的通知,可以對類增強,新增方法或欄位。(知道)
四、spring如何幫我們實現AOP技術(初級版)
spring建立代理工廠bean,屬於半自動,思路跟我們用JDK動態代理和cglib動態代理是一樣的,只是spring通過ioc幫我們建立這些物件,工廠也是由spring提供。唯一的不同是,我們編寫的通知類(之後將其叫成切面類,也就是放增強的程式碼方法的類) 必須實現MethodInterceptor(注意導包時的問題,這個名稱跟cglib中的那個一樣,不要導錯了)介面,並實現invoke方法,這裡就是編寫如何增強切入點,好比上面jdk動態代理和cglib代理中的匿名內部類中的方法一樣。具體下面講解。
匯入jar包
spring : 4個核心 + 1個依賴
spring aop聯盟:
spring aop 實現:
UserServiceImpl
MySpect 切面類,
applicationcontext.xml。
這裡注意,如果沒有介面,那麼其中必須使用3.4的程式碼也就是34行的程式碼,必須告訴spring,使用cglib進行代理,否則出錯。
測試
總結:這種方式屬性半自動的,需要配置工廠bean,並且目標類的確定太小了,每次只能指定一個目標類,如果我們想對別的類也進行加強,那麼就需要在工廠bean中又配置一個目標類,這就體現不出spring的優勢了。不推薦使用這種。瞭解整個發展過程即可
五、spring實現AOP技術(中級 掌握)
全自動過程。直接覆蓋很多目標類,不需要在一個個對目標類進行編寫通知和工廠了。
其他都一樣,配置檔案不一樣。切入點就不單單是一個目標類了。而是一個範圍。
切面類還是跟上面一樣
測試: 成功
總結:相比初級的aop實現,好的地方在不用在自己設定工廠bean了,還在直接大範圍的指定代理目標類,而不是一個類只能被指定一次。這就是全自動,不用我們配置工廠bean
,spring幫我們都做好了。我們只需要關注切面類,也就是通知和切入點的結合即可。
六、使用AspectJ框架(xml方式)實現aop(高階,推薦)
使用這個的好處在於,我們都不需要在切面類中實現什麼通知型別介面了,通知型別介面也在配置檔案中編寫,而切面類只要關注增強的方法程式碼即可。並且不管業務類是否實現介面,編寫的程式碼都是一樣的,如果實現了介面。AspectJ框架會使用jdk代理,如果沒有實現介面,則使用cglib代理,這兩者之間相互轉換。而spring內建的aop技術則需要自己手動去設定,預設是jdk,如果沒有介面,不會自動幫我們轉換。
導包:新增兩個aspect支援的jar包
aop聯盟:com.springsource.org.aopalliance-1.0.0.jar
spring aop支援:spring-aop-3.2.0.RELEASE.jar
aspect 規範:com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
spring aspect 支援:spring-aspects-3.2.0.RELEASE.jar
AspectJ規定通知型別
6個,知道5個,掌握一個
1.before:前置通知(應用:各種校驗)
在方法執行前執行,如果通知丟擲異常,阻止方法執行
2.afterReturning:後置通知(應用:常規資料處理)
方法正常返回後執行,如果方法中丟擲異常,通知無法執行
必須在方法執行後才執行,所以可以獲得方法的返回值。
3.around:環繞通知(應用:十分強大,可以做任何事情) 【掌握】
方法執行前後分別執行,可以阻止方法的執行。要求必須手動的執行目標方法。
4.afterThrowing:丟擲異常通知(應用:包裝異常資訊)
方法丟擲異常後執行,如果方法沒有丟擲異常,無法執行
5.after:最終通知(應用:清理現場)
方法執行完畢後執行,無論方法中是否出現異常
配置檔案
通知型別的詳解
切面類
測試:
總結:使用AspectJ框架實現AOP,好處很多,關注的點只有切面類(不需要實現通知型別介面),切入點範圍,還有通知型別。所以在配置檔案中,也只需要配置這三個關鍵的東西即可,具體看上面的配置檔案
七、使用AspectJ框架(註解方式)實現aop(高階,推薦)
使用註解的話,很簡單,就是把xml方式用註解給替代,其中也就做6件事
1、將切面類配置給spring,相當於<bean id="" class="切面類全限定類名">
@component
2、將切面類申明為切面
@Aspect
3、將目標類配置給spring
@component 如何不編寫名稱的話,那麼要獲取該物件,則使用userServiceImpl進行獲取。
4、申明目標類切入點範圍
1.方法必須private,沒有返回值,沒有引數
2.之後使用將其當成方法呼叫。例如:@After("myPointcut()")
@Pointcut("execution(* com.wuhao.aspectj.annotation.*.*(..))")
5、編寫相應的通知
@Before 前置
@AfterReturning 後置,可以獲得返回值,必須在註解中確定返回值引數名稱。
@AfterThrowing 丟擲異常,可以獲得具體異常資訊,必須在註解確定第二個引數名稱
@Around 環繞[]
@After 最終
6:必須在xml中掃描註解和啟用aop
7、測試
總結;註解的編寫也很簡單,要會寫註解,首先的知道xml是如何實現的,然後一步步用註解來替代xml的內容即可。並且要知道使用AspectJ框架需要配置哪些內容,就三個點,
xml:
目標類bean
切面類bean
<aop:config>
<aop:aspect ref="myAspectId"> //切面
<aop:pointcut expression="execution(* com.wuhao.aspectj.*.*(..))" id="myPointCutId"/> //切入點範圍:<aop:pointcut/>
<aop:around method="myAround" pointcut-ref="myPointCutId"/> //通知型別:<aop:around/>
</aop:config>
八、總結
基本上把spring中使用AOP的技術講清楚了,可能一開始很不習慣,用多了自然就習慣了,並且建議手寫你jdk代理和cglib代理,這樣一步步來,就更容易理解在spring中應用AOP的技術的編寫程式碼了。下一篇還會對spring中使用AOP的技術進一步講解,這一篇相等於寫一個aop的helloworld一樣。