xml中 aop:aspect aop:advisor如何配置
我們知道Spring-AOP的核心執行流程是這樣的.
1. Spring Ioc初始化所有Bean. 如果初始化Bean的時候發現改Bean滿足pointcut中SpEl表示式, 則將該Bean做動態代理.
2. 在Bean的方法執行時,判斷該Bean是否為代理物件,若是代理物件的話, 判斷當前Method是否滿足aop表示式,
3. 如果滿足表示式,則按照順序通知Aop配置的各通知(前,後,環繞,異常等通知)
所以在配置Aop時,我們按照以下步驟:
1. 定義一個Aop通知物件(切面)
2. 配置xml(或者註解), 讓被通知物件與aspect通過表示式進行匹配
準備:
1. 先定義Bean物件
public interface ISleepable {
public void sleep();
}
public class Human implements ISleepable{
@Override
public void sleep() {
System.out.println(" 小主啟動睡覺核心程式...........");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
2. 再定義兩個被通知主體:
public class SleepHelper {
public void afterReturning() throws Throwable {
System.out.println("業餘催眠師 催眠結束, 向家人收錢..........");
}
public void before() throws Throwable {
System.out.println("業餘催眠師 開始催眠..........");
}
}
public class SleepLog {
public void before() throws Throwable {
System.out.println(">>>>>>> 小主準備脫衣睡覺 ");
}
public void afterReturning() throws Throwable {
System.out.println(">>>>>>> 小主睡著了, 自動關燈: ");
}
}
3. 用表示式粘合:
<!--bean物件-->
<bean id="human" class="com.spring_aop.Human"></bean>
<!--兩個通知主體-->
<bean id="yeyuSleepHelper" class="com.spring_aop.SleepHelper"></bean>
<bean id="SleepLog" class="com.spring_aop.SleepLog"></bean>
<aop:config proxy-target-class="true">
<!--1. 定義預設的全域性切點-->
<aop:pointcut expression="execution(* com.spring_aop.Human.*(..))" id="pointCutDefault"/>
<aop:aspect ref="yeyuSleepHelper" id="aspect" order="1">
<aop:before method="before" pointcut-ref="pointCutDefault"/>
<aop:after-returning method="afterReturning" pointcut-ref="pointCutDefault"/>
</aop:aspect>
<aop:aspect ref="SleepLog" id="aspectlog" order="3">
<aop:before method="before" pointcut-ref="pointCutDefault"/>
<aop:after-returning method="afterReturning" pointcut-ref="pointCutDefault"/>
</aop:aspect>
<!--============ 一條美麗的分割線 ==========-->
<aop:aspect ref="yeyuSleepHelper" id="aspect4" order="2">
<!--2. 這裡的pointcut定義在aspect內, -->
<aop:pointcut expression="execution(* com.spring_aop.Human.*(..))" id="pointCutSelfDefined"/>
<aop:before method="before" pointcut-ref="pointCutSelfDefined"/>
<aop:after-returning method="afterReturning" pointcut-ref="pointCutSelfDefined"/>
</aop:aspect>
</aop:config>
注意: 我們將<aop:pointcut>
直接配置在<aop:config>
一級目錄, 表示這是個全域性預設配置,
這樣可以在多個<aop:aspect>
中直接引用此<aop:pointcut>
,
當然也可以在<aop:aspect>
內部單獨定義<aop:pointcut>
,如
<aop:aspect ref="yeyuSleepHelper" id="aspect4" order="2">
<!--2. 這裡的pointcut定義在aspect內, -->
<aop:pointcut expression="execution(* com.spring_aop.Human.*(..))" id="pointCutSelfDefined"/>
<aop:before method="before" pointcut-ref="pointCutSelfDefined"/>
<aop:after-returning method="afterReturning" pointcut-ref="pointCutSelfDefined"/>
</aop:aspect>
備註: 每個<aop:aspect>
都有屬性order
用來定義切面的執行順序, 預設是按照xml解析順序執行
除了直接使用普通Bean定義切面,然後在xml中手動指定其method接受各類別的通知Advice外,我們也可以專門定義一個類來處理接受到的通知. 如:
public class SleepHelperAdvisor implements MethodBeforeAdvice, AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("專業催眠師 催眠結束, 向家人收錢..........");
}
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("專業催眠師 催眠師開始催眠..........");
}
}
注意, 需要實現相應的介面:AfterReturningAdvice
,MethodBeforeAdvice
,
在xml中配置Advisor:
包含三種方式;
第一種:
可以認為advisor 是一種特殊的Aspet, 其內部包含一個Advice實現類和一個Pointcut 表示式,
所以可以直接在<aop:advisor>
中直接配置pointcut表示式.
所以可以如下定義
<aop:config>
<!-- 注意advisor不能放在aspect的後面-->
<aop:advisor advice-ref="professionSleeper"
pointcut="execution(* com.spring_aop.Human.*(..))" order="4"/>
</aop:config>
第二種:
在<aop:advisor>
中 使用已經配置好的pointcut
<aop:config>
<aop:advisor advice-ref="professionSleeper" pointcut-ref="pointCutDefault" order="5"/>
</aop:config>
第三種:
在<aop:advisor>
中 使用自定義的pointcut
<aop:config proxy-target-class="true">
<aop:pointcut id="pointAdvisorDefined" expression="execution(* com.spring_aop.Human.*(..))"/>
<aop:advisor advice-ref="professionSleeper" pointcut-ref="pointAdvisorDefined" order="1"/>
</aop:config>
advisor使用場景
< aop:advisor>大多用於事務管理。
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" timeout="120" propagation="REQUIRED" rollback-for="Exception" />
</tx:attributes>
</tx:advice>
<aop:config proxy-target-class="true">
<aop:pointcut id="txPointCut" expression="..."/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut" />
</aop:config>
小結:
<aop:advisor>
和<aop:aspect>
其實都是將通知和切面進行了封裝,原理基本上是一樣的,只是使用的方式不同而已。
注意點:
- Aop動態代理如下遵從如下規則, 如果bean有介面, 則預設使用Jdk動態代理, 否則使用Cglib動態代理.
這就會出現如下問題:
@Autowired
Human human;
當我們在Spring中自動注入時使用實現類而非介面時, 因為Human有介面,所以會被動態代理為一個實現其介面的代理物件,從而找不到原始的Human, 找出注入失敗.
有兩種解決方法
1. 注入時,使用介面如:
@Autowired
ISleepable human;
2.. 定義動態代理時指定強制使用CgLib, 配置"proxy-target-class="true"
因為Cglib動態代理實現的是繼承機制, 會返回當前類的子類,
最後附錄上全部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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!--bean物件-->
<bean id="human" class="com.spring_aop.Human"></bean>
<!--兩個通知主體-->
<bean id="yeyuSleepHelper" class="com.spring_aop.SleepHelper"></bean>
<bean id="SleepLog" class="com.spring_aop.SleepLog"></bean>
<aop:config proxy-target-class="true">
<!--定義預設的全域性切點-->
<aop:pointcut expression="execution(* com.spring_aop.Human.*(..))" id="pointCutDefault"/>
<aop:aspect ref="yeyuSleepHelper" id="aspect" order="1">
<aop:before method="before" pointcut-ref="pointCutDefault"/>
<aop:after-returning method="afterReturning" pointcut-ref="pointCutDefault"/>
</aop:aspect>
<aop:aspect ref="SleepLog" id="aspectlog" order="3">
<aop:before method="before" pointcut-ref="pointCutDefault"/>
<aop:after-returning method="afterReturning" pointcut-ref="pointCutDefault"/>
</aop:aspect>
<!--============ 一條美麗的分割線 ==========-->
<aop:aspect ref="yeyuSleepHelper" id="aspect4" order="2">
<!--這裡的pointcut定義在aspect內, -->
<aop:pointcut expression="execution(* com.spring_aop.Human.*(..))" id="pointCutSelfDefined"/>
<aop:before method="before" pointcut-ref="pointCutSelfDefined"/>
<aop:after-returning method="afterReturning" pointcut-ref="pointCutSelfDefined"/>
</aop:aspect>
</aop:config>
<bean id="professionSleeper" class="com.spring_aop.SleepHelperAdvisor"></bean>
<aop:config>
<!--advisor不能放在aspect的後面-->
<aop:advisor advice-ref="professionSleeper" pointcut="execution(* com.spring_aop.Human.*(..))" order="4"/>
<aop:advisor advice-ref="professionSleeper" pointcut-ref="pointCutDefault" order="5"/>
</aop:config>
</beans>
完…