1. 程式人生 > >xml中 aop:aspect aop:advisor如何配置

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>其實都是將通知和切面進行了封裝,原理基本上是一樣的,只是使用的方式不同而已。

注意點:

  1. 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>

完…