在應用開發中,有不少相似日誌、安全和事務管理的功能。這些功能都有一個共同點,那就是不少個對象都須要這些功能。複用這些通用的功能的最簡單的方法就是繼承或者委託。可是當應用規模達到必定程度時,使用繼承或委託將會使應用的結構很是複雜。spring
面向切面即是解決上面問題的最佳辦法。咱們把這些通用的功能(橫切關注點)放在專門的類中(這種類又叫切面),而後在程序運行後經過動態代理,將這些功能插入到須要這些功能的類中。在這種模式下,這些通用的功能都是經過切面統一管理,使得模塊之間更加清晰。安全
(一)AOP術語maven
在學習Spring的面向切面時,首先須要學習Spring的AOP術語。在切面中經常使用的術語有通知(advice)、切點(pointcut)和鏈接點(joinpoint)。 ide
1.通知(Advice)學習
當咱們往那些須要插入通用功能的類中進行插入時,通知就是定義這個功能是什麼以及在何時插入,是方法調用以前?以後?仍是先後都要用?測試
前置通知:在目標方法調用以前調用通知功能。spa
後置通知:在目標方法調用以後調用通用功能,此時不關心方法的輸出是什麼。3d
返回通知:在目標方法成功執行後調用通知。代理
異常通知:在目標方法拋出異常後調用通知。rest
環繞通知:在目標方法的先後執行自定義的行爲。
2.鏈接點(Join point)
鏈接點就是全部能夠插入功能的點的集合,例如,調用方法時、拋出異常時、修改一個字段時。Spring只支持方法級別的鏈接點。
3.切點(Pointcut)
咱們知道,不少的鏈接點均可以進行插入功能,可是隻有在須要這個功能地方進行插入纔會有意義,而這些「須要」的地方,就是切點。
上面說到通知定義了功能是什麼以及在何時插入。那麼切點就是定義在什麼地方插入。
4.切面(Aspect)
切面就是通知和切點的結合,他們一塊兒定義了功能是什麼以及何時、什麼地方插入。
5.引入(Introduction)
引入容許咱們向現有的類添加新方法或屬性(實際上是經過動態代理,生成一個和原有類有同樣方法的類,同時在這個類中添加新方法或屬性。由於方法同樣,咱們徹底能夠把新生成的類當作是原來的類)。這樣咱們就能夠在不修改類的狀況下讓他有新的行爲和狀態。
6.織入(Weaving)
織入就是把切面應用到目標對象的過程,就是前面說的「插入」。
(二)Spring中使用AOP的幾種方式
Spring提供四種AOP支持
(一)使用註解建立切面
若是使用AspectJ的註解,須要aspectjrt和aspectjweaver依賴。
切面就是一個特殊的類,之因此特殊是由於要在類上添加@Aspect註解。而且這個切面也須要是一個bean。
下面咱們根據一個實例來了解切面的建立,在這個實例中,咱們要經過AOP的技術,使得全部的汽車在啓動的先後都須要進行安全檢查。
首先須要一個接口來構建一個車的骨架。
有公交車和貨車都實現了這個接口。
咱們的目的是讓全部實現Car接口的車在啓動先後都能進行安全檢查,例如啓動前車門狀況,跑完鎖是否關了等,下面是AOP的實現。
當咱們把切面聲明完之後,還須要啓動AspectJ自動代理,不然就不會生效。
在JavaConfig中,啓動須要添加@EnableAspectJAutoProxy註解
在XML中,須要Spring Aop空間的<aop:aspectj-autoproxy>元素
下面是測試代碼,測試使用了:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {Config.class}) public class CarTest { @Autowired private Car bus; @Autowired private Car trucks; @Test public void go(){ bus.run(); bus.change(1); bus.change(2); System.out.println("--------"); trucks.run(); } }
我是使用maven建立的項目,下面是依賴:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.12.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/junit/junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-test --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>4.3.10.RELEASE</version> <scope>test</scope> </dependency> <!-- https://mvnrepository.com/artifact/org.hamcrest/hamcrest-core --> <dependency> <groupId>org.hamcrest</groupId> <artifactId>hamcrest-core</artifactId> <version>1.3</version> <scope>test</scope> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-aop --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>4.3.11.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjrt --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.8.10</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.4</version> </dependency>
(一)處理通知中的參數
當咱們建立通知時,可能會須要切入點方法調用時的參數。例如,在計算汽車油量時,通知應該在汽車換擋時獲取切換的檔數,而後根據檔數來計算油耗。
在這個例子中,經過args(num)限定符實現了參數的傳遞。它說明change()方法的int參數會被傳遞到通知中去。參數的名稱a須要與切點方法簽名中的參數相匹配。