Java Spring AOP用法
Spring AOP
Java web 環境搭建
Java web 項目搭建
Java Spring IOC用法
spring提供了兩個核心功能,一個是IoC(控制反轉),另外一個便是Aop(面向切面編程),IoC有助於應用對象之間的解耦,AOP則可以實現橫切關注點(如日志、安全、緩存和事務管理)與他們所影響的對象之間的解耦。
1.簡介
AOP主要包含了通知、切點和連接點等術語,介紹如下
- 通知(Advice)
通知定義了切面是什么以及何時調用,何時調用包含以下幾種
Before 在方法被調用之前調用通知
After 在方法完成之后調用通知,無論方法執行是否成功
After-returning 在方法成功執行之后調用通知
After-throwing 在方法拋出異常后調用通知
Around 通知包裹了被通知的方法,在被通知的方法調用之前和調用之后執行自定義的行為
-
切點(PointCut)
通知定義了切面的什么和何時,切點定義了何處,切點的定義會匹配通知所要織入的一個或多個連接點,我們通常使用明確的類的方法名稱來指定這些切點,或是利用正則表達式定義匹配的類和方法名稱來指定這些切點。
切點的格式如下execution(* com.ganji.demo.service.user.UserService.GetDemoUser (..) )
-
連接點(JoinPoint)
連接點是在應用執行過程中能夠插入切面的一個點,這個點可以是調用方法時,拋出異常時,甚至是修改一個字段時,切面代碼可以利用這些連接點插入到應用的正常流程中,並添加新的行為,如日志、安全、事務、緩存等。
現階段的AOP框架
AOP框架除了Spring AOP之外,還包括AspectJ、JBoss AOP;
上述框架的區別是Spring AOP只支持到方法連接點,另外兩個還支持字段和構造器連接點。
2.用法
同依賴注入一樣,AOP在spring中有兩種配置方式,一是xml配置的方式,二是自動注解的模式。
-
2.1 xml中聲明切面
- 2.1.1 AOP配置元素
在xml中,我們使用如下AOP配置元素聲明切面
AOP配置元素 | 描述 ------------ | ------------- `<aop:advisor>` | 定義AOP通知器 `<aop:after>` | 定義AOP后置通知(不管該方法是否執行成功) `<aop:after-returning>` | 在方法成功執行后調用通知 `<aop:after-throwing>` | 在方法拋出異常后調用通知 `<aop:around>` | 定義AOP環繞通知 `<aop:aspect>` | 定義切面 `<aop:aspect-autoproxy>` | 定義`@AspectJ`注解驅動的切面 `<aop:before>` | 定義AOP前置通知 `<aop:config>` | 頂層的AOP配置元素,大多數的<aop:*>包含在<aop:config>元素內 `<aop:declare-parent>` | 為被通知的對象引入額外的接口,並透明的實現 `<aop:pointcut>` | 定義切點
- 2.1.2 定義切面
我們在service層添加
com.ganji.demo.service.aspect.XmlAopDemoUserLog
類,里面實現了攔截方法,具體如下package com.ganji.demo.service.aspect; import org.aspectj.lang.ProceedingJoinPoint; /** * Created by admin on 2015/9/2. */ public class XmlAopDemoUserLog { // 方法執行前通知 public void beforeLog() { System.out.println("開始執行前置通知 日志記錄"); } // 方法執行完后通知 public void afterLog() { System.out.println("開始執行后置通知 日志記錄"); } // 執行成功后通知 public void afterReturningLog() { System.out.println("方法成功執行后通知 日志記錄"); } // 拋出異常后通知 public void afterThrowingLog() { System.out.println("方法拋出異常后執行通知 日志記錄"); } // 環繞通知 public Object aroundLog(ProceedingJoinPoint joinpoint) { Object result = null; try { System.out.println("環繞通知開始 日志記錄"); long start = System.currentTimeMillis(); //有返回參數 則需返回值 result = joinpoint.proceed(); long end = System.currentTimeMillis(); System.out.println("總共執行時長" + (end - start) + " 毫秒"); System.out.println("環繞通知結束 日志記錄"); } catch (Throwable t) { System.out.println("出現錯誤"); } return result; } }
- 2.1.3 xml聲明切面並調用
我們在web層,
web-inf/dispatcher-servlet.xml
中定義切面,具體如下<!--定義切面 指定攔截方法時 做什么--> <bean id="xmlAopDemoUserLog" class="com.ganji.demo.service.aspect.XmlAopDemoUserLog"></bean> <aop:config> <aop:aspect ref="xmlAopDemoUserLog"> <!--指定切面--> <!--定義切點--> <aop:pointcut id="logpoint" expression="execution(* com.ganji.demo.service.user.UserService.GetDemoUser(..))"></aop:pointcut> <!--定義連接點--> <aop:before pointcut-ref="logpoint" method="beforeLog"></aop:before> <aop:after pointcut-ref="logpoint" method="afterLog"></aop:after> <aop:after-returning pointcut-ref="logpoint" method="afterReturningLog"></aop:after-returning> <aop:after-throwing pointcut-ref="logpoint" method="afterThrowingLog"></aop:after-throwing> </aop:aspect> </aop:config>
在controller下調用,調用具體如下
DemoUserEntity demoUser=userService.GetDemoUser(1);
這是運行起來 我們將看到打印出如下日志
開始執行前置通知 日志記錄
開始執行后置通知 日志記錄
方法成功執行后通知 日志記錄- 2.1.4 小結
如果通過xml配置,我們還可以實現環繞通知,環繞通知的目的是把前置通知和后置通知的信息共享起來。同時還可以為通知傳遞方法的參數,在切面攔截中驗證參數的有效性。
-
2.2 自動注解AOP
在上述2.1中我們通過xml配置的形式 實現了AOP編程,現在我們通過不配置xml,配置注解的形式實現AOP。
- 2.2.1 配置自動代理
使用配置注解,首先我們要將切面在spring上下文中聲明成自動代理bean,我們需要在web層的
web-inf/dispatcher-servlet.xml
文件中配置如下一句話即可<aop:aspectj-autoproxy />
當然我們需要在xml的根目錄beans下引用aop的命名空間和xsi
xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"
- 2.2.2 使用
@Aspect
注解
聲明一個切面,只需要在類名上添加
@Aspect
屬性即可,具體的連接點,我們用@Pointcut
和@Before
、@After
等標注。具體如下
在聲明前 我們需要依賴配置pom<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.6.11</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.6.11</version> </dependency>
聲明切面類,包含了注解
@Aspect
以及何時(如@Before
)執行通知package com.ganji.demo.service.aspect; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Service; /** * Created by admin on 2015/9/2. */ @Aspect @Service public class XmlAopDemoUserLog { // 配置切點 及要傳的參數 @Pointcut("execution(* com.ganji.demo.service.user.UserService.GetDemoUser(..)) && args(id)") public void pointCut(int id) { } // 配置連接點 方法開始執行時通知 @Before("pointCut(id)") public void beforeLog(int id) { System.out.println("開始執行前置通知 日志記錄:"+id); } // 方法執行完后通知 @After("pointCut(id)") public void afterLog(int id) { System.out.println("開始執行后置通知 日志記錄:"+id); } // 執行成功后通知 @AfterReturning("pointCut(id)") public void afterReturningLog(int id) { System.out.println("方法成功執行后通知 日志記錄:"+id); } // 拋出異常后通知 @AfterThrowing("pointCut(id)") public void afterThrowingLog(int id) { System.out.println("方法拋出異常后執行通知 日志記錄"+id); } // 環繞通知 @Around("pointCut(id)") public Object aroundLog(ProceedingJoinPoint joinpoint,int id) { Object result = null; try { System.out.println("環繞通知開始 日志記錄"+id); long start = System.currentTimeMillis(); //有返回參數 則需返回值 result = joinpoint.proceed(); long end = System.currentTimeMillis(); System.out.println("總共執行時長" + (end - start) + " 毫秒"); System.out.println("環繞通知結束 日志記錄"); } catch (Throwable t) { System.out.println("出現錯誤"); } return result; } }
- 2.2.3 總結
按照上述兩個步驟,使用注解實現Aop即可,這里依賴了IOC。