Throw Advice

若 想要在例外發生時通知某些服務物件作某些事,您可以使用Throws Advice,在Spring中想要實作Throws Advice,必須實作org.springframework.aop.ThrowsAdvice介面,然而這個介面並沒有定義任何的方法,它只是一個 標籤介面(Tag interface),您可以在當中定義任意的afterThrowing方法名稱,只要它是以下的形式: afterThrowing([Method], [args], [target], Throwable subclass)

方括號[]中的設定,例如Method、args與target等表示是可以省略的,方法中一定要的是subclass,也就是這個參數必須是 Throwable的子類別,在例外發生時,會檢驗所設定的Throws Advice中是否有符合例外類型的方法,如果有的話就通知它執行,以下是兩個方法宣告的例子:

   void afterThrowing(Throwable throwable);
   void afterThrowing(Method method, Object[] args, 
                          Object target, Throwable throwable);

您在方法上如果宣告不同的Throwable型態,則依例外發生類型的不同,會通知不同的方法,例如SomeException會通知宣告有SomeException參數的方法,而OtherException會通知宣告有OtherException的方法。

注意到當例外發生時,Throw Advice的任務只是呼叫對應的方法,您並不能在Throws Advice中將例外處理掉,在Throw Advice執行完畢後,原先的例外仍被傳播至應用程式之中,Throw Advice並不介入應用程式的例外處理,例外處理仍舊是應用程式本身所要負責的,如果您想要在Throw Advice處理時中止應用程式的處理流程,作法是丟出其它的例外。

來看個Throws Advice的實際例子,首先定義IHello介面:

IHello.java

   package onlyfun.caterpillar;
   
   public interface IHello {
       public void hello(String name) throws Throwable;
   } 

接著定義一個HelloSpeaker類別來實作IHello介面,並在hello()方法當中模擬程式發生錯誤時的例外丟出: HelloSpeaker.java

   package onlyfun.caterpillar;
   
   public class HelloSpeaker implements IHello {
       public void hello(String name) throws Throwable {                           
           System.out.println("Hello, " + name);
           // 抱歉!程式錯誤!發生例外XD
           throw new Exception("發生例外...");
       }
   }

如果您需要在應用程式丟出例外時,介入Throw Advice以提供一些服務,,例如記錄下一些例外資訊,則您可以實作ThrowAdvice介面: SomeThrowAdvice.java

   package onlyfun.caterpillar;
   
   import java.lang.reflect.Method;
   import java.util.logging.Level;
   import java.util.logging.Logger;
   import org.springframework.aop.ThrowsAdvice;
   
   public class SomeThrowAdvice implements ThrowsAdvice { 
       private Logger logger = 
               Logger.getLogger(this.getClass().getName()); 
       public void afterThrowing(Method method, Object[] args, 
                                 Object target, Throwable subclass) { 
           // 記錄例外 
           logger.log(Level.INFO, 
                   "Logging that a " + subclass + 
                   "Exception was thrown in " + method); 
       } 
   }

接著在Bean定義檔中寫下以下的定義,以讓Throw Advice在例外發生時提供記錄服務: beans-config.xml

   <?xml version="1.0" encoding="UTF-8"?> 
   <!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN" 
     "http://www.springframework.org/dtd/spring-beans.dtd"> 
   
   <beans> 
   <bean id="someThrowAdvice" 
         class="onlyfun.caterpillar.SomeThrowAdvice"/>
   
   <bean id="helloSpeaker" 
         class="onlyfun.caterpillar.HelloSpeaker"/> 
   
   <bean id="helloProxy" 
         class="org.springframework.aop.framework.ProxyFactoryBean"> 
       <property name="proxyInterfaces"> 
           <value>onlyfun.caterpillar.IHello</value> 
       </property> 
       <property name="target"> 
           <ref bean="helloSpeaker"/> 
       </property> 
       <property name="interceptorNames"> 
           <list> 
               <value>someThrowAdvice</value> 
           </list> 
       </property> 
   </bean> 
   </beans>


可以撰寫以下的程式來測試一下Throw Advice是否如預期般運作: SpringAOPDemo.java

   package onlyfun.caterpillar;
   
   import org.springframework.context.ApplicationContext;
   import org.springframework.context.
             support.FileSystemXmlApplicationContext;
   
   public class SpringAOPDemo {
       public static void main(String[] args) {
           ApplicationContext context = 
               new FileSystemXmlApplicationContext(
                       "beans-config.xml"); 
       IHello helloProxy = 
           (IHello) context.getBean("helloProxy"); 
       
       try { 
           helloProxy.hello("Justin"); 
       } 
       catch(Throwable throwable) { 
           // 應用程式的例外處理
           System.err.println(throwable);
       } 
       }
   } 

在程式中,應用程式本身定義了例外處理的邏輯,Throw Advice不介面應用程式處理例外的邏輯,Throw Advice提供的是額外的記錄服務。