Spring AOP 後篇(三): AOP切面程式設計
阿新 • • 發佈:2018-12-10
Spring AOP 後篇: AOP切面程式設計
該文章參考多篇文章的基礎上進行了簡化並做少許修改,方便理解。原文章地址如下:
一、理解切入點表示式(execution())(重點)
-
切入點指示符
execution
:用於匹配方法執行連線點。這是使用Spring AOP時使用的主要切入點指示符。within
:限制匹配某些型別中的連線點(使用Spring AOP時在匹配型別中宣告的方法的執行)。this
:限制與連線點的匹配(使用Spring AOP時執行方法),其中bean引用(Spring AOP代理)是給定型別的例項。target
:限制匹配連線點(使用Spring AOP時執行方法),其中目標物件(被代理的應用程式物件)是給定型別的例項。args
:限制與連線點的匹配(使用Spring AOP時執行方法),其中引數是給定型別的例項。@target
:限制與連線點的匹配(使用Spring AOP時執行方法),其中執行物件的類具有給定型別的註釋。@args
:限制與連線點的匹配(使用Spring AOP時執行方法),其中傳遞的實際引數的執行時型別具有給定型別的註釋。@within
:限制匹配到具有給定註釋的型別中的連線點(使用Spring AOP時執行在具有給定註釋的型別中宣告的方法)。@annotation
:限制連線點的匹配,其中連線點的主題(在Spring AOP中執行的方法)具有給定的註釋。
within、this、args等詳細解釋。。。(todo)
-
execution表示式 詳解 (參考Spring AOP 官方文件)
<!-- * 號表示萬用字元; 如下 execution表示式表示 匹配service包下的所有類的所有帶參及不帶引數的方法 --> <aop:pointcut id="businessServcie" expression="execution(* com.xyz.myapp.service.*.*(..))"
- execution() : 表示式主體
- 第一個’*'號位置:表示 返回型別;*作用是匹配任何返回型別;
- com.xyz.myapp.service: 表示包名,即service包
- 第二個’*'號位置:表示 類名;*作用是匹配該包下的所有類
- 第三個‘*’號位置:表示 方法名; *作用是匹配所有方法
- (…): 表示 括號裡面代表引數;()表示匹配一個不帶引數的方法;(…)表示匹配任何數量(零個或多個)引數;(*)表示匹配一個採用任何型別引數的方法
-
常用切入點表示式(execution());Spring AOP官方文件
- 執行任何公共方法:
execution(public * *(..))
- 執行名稱以以下開頭的任何方法
set
:
execution(* set*(..))
- 執行
AccountService
介面定義的任何方法:
execution(* com.xyz.service.AccountService.*(..))
- 執行
service
包中定義的任何方法:
execution(* com.xyz.service.*.*(..))
- 執行服務包或其子包中定義的任何方法:
execution(* com.xyz.service..*.*(..))
- 服務包中的任何連線點(僅在Spring AOP中執行方法):
within(com.xyz.service.*)
- 服務包或其子包中的任何連線點(僅在Spring AOP中執行方法):
within(com.xyz.service..*)
- 代理實現
AccountService
介面的任何連線點(僅在Spring AOP中執行方法) :
this(com.xyz.service.AccountService)
二、XML手動配置方式
- aopBean.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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">
<!-- dao 例項 -->
<bean id="userDao" class="com.aop.UserDao" />
<bean id="orderDao" class="com.aop.OrderDao" />
<!-- 切面類:在呼叫dao類時,先後呼叫 時間列印及日誌列印類 -->
<bean id="timePrint" class="com.aop.TimePrint" />
<bean id="logPrint" class="com.aop.LogPrint" />
<aop:config>
<!-- 定義切面類
ref: 指定切面類;
order(int):指定切面的優先順序來控制通知的執行順序; order1 》order2
-->
<aop:aspect id="time" ref="timePrint" order="1">
<!-- 切入點
expression: 定義切入點表示式;即定義要切入的類或方法
-->
<aop:pointcut id="addTime" expression="execution(* com.aop.userDao.*(..))" />
<!-- 前置通知: 在目標方法呼叫前執行
method:切面類執行的方法;即目標類呼叫前執行的方法
-->
<aop:before method="printTime" pointcut-ref="addTime" />
<!-- 後置通知: 在目標方法呼叫後執行 -->
<aop:after method="printTime" pointcut-ref="addTime" />
</aop:aspect>
<aop:aspect id="log" ref="logPrint" order="2">
<aop:pointcut id="printLog" expression="execution(* com.aop.*.*(..))" />
<aop:before method="LogBefore" pointcut-ref="printLog" />
<aop:after method="LogAfter" pointcut-ref="printLog" />
</aop:aspect>
</aop:config>
</beans>
-
aopBean.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:p="http://www.springframework.org/schema/p" 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"> <!-- dao 例項 --> <bean id="userDao" class="com.aop.dao.UserDao"></bean> <bean id="orderDao" class="com.aop.dao.OrderDao"></bean> <!-- 切面類 --> <bean id="aop" class="com.aop.dao.Aop"></bean> <!-- Aop配置 --> <aop:config> <!-- 定義一個切入點表示式: 攔截哪些方法 --> <aop:pointcut id="pt" expression="execution(* com.aop.dao.*.*(..))"/> <!-- 切面 --> <aop:aspect ref="aop"> <!-- 環繞通知 --> <aop:around method="around" pointcut-ref="pt"/> <!-- 前置通知: 在目標方法呼叫前執行 --> <aop:before method="begin" pointcut-ref="pt"/> <!-- 後置通知: --> <aop:after method="after" pointcut-ref="pt"/> <!-- 返回後通知 --> <aop:after-returning method="afterReturning" pointcut-ref="pt"/> <!-- 異常通知 --> <aop:after-throwing method="afterThrowing" pointcut-ref="pt"/> </aop:aspect> </aop:config> </beans>
三、@Aspectj 註解方式
-
bean.xml 配置aop註解掃描
<?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:p="http://www.springframework.org/schema/p" 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="cn.itcast.e_aop_anno"></context:component-scan> <!-- 開啟aop註解方式 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
-
使用切面類使用@Aspect 註解
@Component //加入IOC容器 @Aspect // 指定當前類為切面類 public class Aop { // 指定切入點表示式: 攔截哪些方法; 即為哪些類生成代理物件 //解釋@Pointcut("execution(* cn.itcast.e_aop_anno.*.*(..))") //@Pointcut("execution(* 切入點表示式固定寫法, cn.itcast.e_aop_anno表示包.類名(可以用*表示包下所有的類).方法名(可以用*表示類下所有的方法)(..)表示引數可以用.. @Pointcut("execution(* cn.itcast.e_aop_anno.*.*(..))") public void pointCut_(){ } //@Before("execution(* cn.itcast.e_aop_anno.*.*(..))")每個方法需要寫相同的引用,所以將相同的部分抽取到一個空的方法中pointCut_(), // 前置通知 : 在執行目標方法之前執行 @Before("pointCut_()") public void begin(){ System.out.println("開始事務/異常"); } // 後置/最終通知:在執行目標方法之後執行 【無論是否出現異常最終都會執行】 @After("pointCut_()") public void after(){ System.out.println("提交事務/關閉"); } // 返回後通知: 在呼叫目標方法結束後執行 【出現異常不執行】 @AfterReturning("pointCut_()") public void afterReturning() { System.out.println("afterReturning()"); } // 異常通知: 當目標方法執行異常時候執行此關注點程式碼 @AfterThrowing("pointCut_()") public void afterThrowing(){ System.out.println("afterThrowing()"); } // 環繞通知:環繞目標方式執行 @Around("pointCut_()") public void around(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("環繞前...."); pjp.proceed(); // 執行目標方法 System.out.println("環繞後...."); } }
-