最愛Java

          書山有路勤為徑,學海無涯苦作舟

          《AspectJ Cookbook》讀書筆記二十: 應(yīng)用類和組件級方面

          一.驗證傳遞給方法的參數(shù)
                  創(chuàng)建一個模塊化參數(shù)檢查邏輯的方面。聲明一個切入點,用于捕獲其中將檢查參數(shù)的方法的執(zhí)行。切入點應(yīng)該把參數(shù)展示給相應(yīng)的通知使得它可以執(zhí)行檢查。
                  依賴于參數(shù)檢查的結(jié)果,通知將繼續(xù)執(zhí)行方法,或者每當參數(shù)不合適時都重寫方法。
                  傳統(tǒng)的參數(shù)驗證和檢查代碼:
          package com.aspectj;

          import java.net.URL;
          import java.net.MalformedURLException;

          public class TraditionalMainApplication
          {
             
          private static final String COMMAND_LINE_USAGE = "MyAppliction usage :\n\n"
                   
          + "\tjava MainApplication <url>";

             
          public static void main(String[] args)
             
          {
                
          if (args.length == 1)
                
          {
                   
          try
                   
          {
                      
          // Assuming that the first argument supplied is a url
                      
          // Concentrating on the business logic, not validation which
                      
          // is handled by the aspect.
                      URL url = new URL(args[0]);

                      System.out.println(
          "Application Started, doing stuff with " + url);
                   }

                   
          catch (MalformedURLException mue)
                   
          {
                      System.err.println(COMMAND_LINE_USAGE);
                      System.err.println(
          "Please enter a valid URL for <url>");
                   }

                }

                
          else
                
          {
                   System.err.println(COMMAND_LINE_USAGE);
                }

             }

          }

              通過方面的應(yīng)用,可以改成如下:
          package com.aspectj;

          import java.net.URL;
          import java.net.MalformedURLException;

          public aspect VerifyMethodArgsAspect
          {
             
          private static final String COMMAND_LINE_USAGE = "MyAppliction usage :\n\n" +
             
          "\tjava MainApplication <url>";
             
             
          public pointcut captureMain(String[] arguments) : 
                execution(
          void MainApplication.main(String[])) && args(arguments);

             
          public pointcut createURLCalledinMainMethod() : call(java.net.URL.new(..)) && withincode(public void MainApplication.main(String[]));
             
             
          void around(String[] arguments) : captureMain(arguments)
             
          {
                
          if (arguments.length == 1)
                
          {
                    
          // Test that the host and port are valid
                   try
                   
          {
                      URL url 
          = new URL(arguments[0]);
                      proceed(arguments);
                   }

                   
          catch(MalformedURLException mfe)
                   
          {
                      System.err.println(COMMAND_LINE_USAGE);
                      System.err.println(
          "Please enter a valid URL for <url>");
                   }

                }

                
          else
                
          {
                   System.err.println(COMMAND_LINE_USAGE);
                }

             }

             
             
          // If necessary soften the exception that would normally have been raised
             
          // if the url parameter was badly formed, but only in the validated main method
             declare soft : MalformedURLException : createURLCalledinMainMethod();

          }


          package com.aspectj;

          import java.net.URL;

          public class MainApplication
          {

             
          public static void main(String[] args)
             
          {
                
          // Assuming that the first argument supplied is a url
                
          // Concentrating on the business logic, not validation which
                
          // is handled by the aspect.
                URL url = new URL(args[0]);
                
                System.out.println(
          "Application Started, doing stuff with " + url);
             }

          }

          二. 重寫在構(gòu)造函數(shù)調(diào)用上實例化的類
          public static void traditionalObjectOrentedImplementationSelection() {
              MyInterface myObject 
          = new MyClass() //Specifies the MyClass implementation of the MyInterface interface
              System.out.println(myObject);
              myObject.foo();
          }

              以上代碼為了改變MyInterface的實現(xiàn),必須把代碼改成:
                  MyIntrface myObject = new AnotherClass();
              通過使用AspectJ的call(Signature)切入點和around()通知,可以使得事情更簡單,更整潔。如:
          package com.aspectj;

          public aspect ControlClassSelectionAspect 
          {
             
          public pointcut myClassConstructor() : call(MyClass.new());
             
             Object around() : myClassConstructor()
             
          {
                
          return new AnotherClass();
             }

             
             
          // Runtime selection variation
             /*
             Object around() : myClassConstructor() && 
                               if (System.getProperty("select_class").equals("AnotherClass"))
             {
                return new AnotherClass();
             }
             
          */

          }
                  ControlClassSelectionAspect聲明了myClassConstructor()切入點,它會截獲調(diào)用,以實例化MyClass對象。相應(yīng)的around()通知然后會返回重寫AnotherClass類的一個新實例。
                  進一步,可以檢查用戶定義的運行時參數(shù):
                  Object aound():myClassConstructor()&&if (System.getProperty("select_class")).equals("AnotherClass") {
                      return new AnotherClass();
                  }

          三.添加持久性到類中
                  可以把這個抽象方面擴展成一些特殊化的子方面,他們將會應(yīng)用程序內(nèi)的每個持久對象集合實現(xiàn)一個合適的持久性機制。
          package com.aspectj;

          public abstract aspect PersistenceAspect
          {
             
          public interface ObjectStore
             
          {
                
          public void persist();
                
          public void restore();
             }

             
             
          protected abstract pointcut restoreStorage(ObjectStore store);
             
             after(ObjectStore store) : restoreStorage(store)
             
          {
                store.restore();
             }

             
             
          protected abstract pointcut persistStorage(ObjectStore store);
             
             after(ObjectStore store) : persistStorage(store)
             
          {  
                store.persist();
             }

          }


                  PersistenceAspect抽象方面把ObjfctStore角色定義為接口,可以將其應(yīng)用與任何類,來管理對象集合的持久性。restoreStorage(ObjectStore)和persistStorage(ObjectStore)抽象切入點通過特殊化的子方面來實現(xiàn),用于觸發(fā)對應(yīng)的after()通知塊,它將恢復(fù)或保持指定的ObjectStore.
                  ObjectStore接口中指定的restore()和persist()方法是依據(jù)特地ingde持久性策略實現(xiàn)的,該策略用于應(yīng)用程序內(nèi)的對象集合,使得可以基于每個ObjectStore來改變持久性策略。
          package com.aspectj;

          import java.io.*;

          public aspect EmployeePersistenceAspect extends PersistenceAspect
          {
             declare parents : EmployeeCollection 
          implements ObjectStore;

             
          protected pointcut restoreStorage(ObjectStore store) : 
                execution(EmployeeCollection.
          new(..)) && 
                target(store);
             
             
          protected pointcut persistStorage(ObjectStore store) : 
                call(
          * java.util.List.add(..)) && 
                target(EmployeeCollection) 
          &&
                target(store);

             declare parents : Employee 
          extends Serializable;
             
             
          private File EmployeeCollection.employeesFile = new File("employees.ser");
             
             
          public void EmployeeCollection.persist()
             
          {
                
          try
                
          {
                   ObjectOutput out 
          = new ObjectOutputStream(new FileOutputStream(this.employeesFile));
                   Object[] objectsToStore 
          = this.toArray();
                   out.writeObject(objectsToStore);
                   out.flush();
                   out.close();
                }

                
          catch (Exception e)
                
          {
                   System.err.println(
          "Couldn't store employees to " + this.employeesFile);
                }

             }

             
             
          public void EmployeeCollection.restore()
             
          {
                
          // Check that the serialized accounts file exists
                if (this.employeesFile.exists() && this.employeesFile.canRead())
                
          {
                   
          try
                   
          {
                      ObjectInput input 
          = new ObjectInputStream(new FileInputStream(this.employeesFile));
                      
                      Object[] objectsToRestore 
          = (Object[]) input.readObject();
                      
          for(int x = 0; x < objectsToRestore.length; x++)
                      
          {
                         
          this.add(objectsToRestore[x]);
                      }

                      
                      input.close();
                   }

                   
          catch (Exception e)
                   
          {
                      System.err.println(
          "Couldn't restore employees due to a corrupt " + this.employeesFile + " file");
                      e.printStackTrace();
                   }

                }

             }

          }


                  EmployeePersistenceAspect將ObjectStore接口應(yīng)用于EmployeeCollection類。然后實現(xiàn)restoreStorage(ObjectStore)方面,以捕獲何時構(gòu)造EmployeeCollection,并使用target(TypePattern || Identifier)切入點將EmployeeCollection展示為要存儲的ObjectStore.
                  實現(xiàn)persistStorage(ObjectStore)切入點,用以捕獲無論何時更改EmployeeCollection,并在此時保持ObjectStore的內(nèi)容。EmployeeCollection是ArrayList的特殊化;為了避免直接織入到Java標準庫中,使用call(Signature)切入點來捕獲何時在List上調(diào)用add(...)方法。
                  不過,call(Signature)切入點定義本質(zhì)上過于普通,因此必須將通過persistStorage(ObjectStore)切入點捕獲的連接點限制于只捕獲何時在EmployeeCollection上調(diào)用add(...)。為了應(yīng)用這種限制,第一個target(TypePattern || Identifier)切入點使用TypePattern來指定你只對其目標是EmployeeCollection類的連接點感興趣。第二個target(TypePattern || Identifier)切入點使用一個標識符將當前ObjectStore傳遞給通知塊,作為persistStorage(ObjectStore)切入點的單一參數(shù)。
                  最后,將直觀的對象串行化持久性策略應(yīng)用于EmployeeCollction。EmployeeCollection中的每個對象都是Employee類的一個實例,并且擴展這個類以實現(xiàn)Serializable接口,使得可對它應(yīng)用標準的Java對象串行化技術(shù)。串行化的Employee對象將存儲在一個文件中;因此,將以employeesFile屬性的形式把這個文件信息添加到EmployeeCollection類中。
                  為了完成圖形,將把persist()和restore()方法實現(xiàn)添加到EmployeeCollection類中,使得它可以滿足ObjectStore接口所需的行為。這些方法會執(zhí)行Employee對象的串行化和恢復(fù),這是通過employeesFile屬性指定的文件來進行的。
                  以下顯示了抽象的PersistenceAspect替代實現(xiàn)的一部分,它只會在關(guān)閉應(yīng)用程序關(guān)閉時保持其對應(yīng)的ObjectStore.
          package com.aspectj;

          import java.io.*;

          public privileged aspect AccountPersistenceAspect extends PersistenceAspect
          {
             declare parents : MainApplication 
          implements ObjectStore, Runnable;

             
          protected pointcut restoreStorage(ObjectStore store) : 
                execution(MainApplication.
          new(..)) && 
                target(store);
             
             
          // Selects all join points where it is necessary to persist the store
             protected pointcut persistStorage(ObjectStore store) : 
                execution(
          public void MainApplication.run()) && 
                
          this(store);
             
             declare parents : Account 
          extends Serializable;

             
          private File MainApplication.accountsFile = new File("accounts.ser");
             
             after(MainApplication mainApplication) : 
                restoreStorage(ObjectStore) 
          && 
                target(mainApplication)
             
          {
                
          // Register a shutdown hook
                Thread shutdownThread = new Thread(mainApplication);
                Runtime.getRuntime().addShutdownHook(shutdownThread);
             }

             
             
          public void MainApplication.run()
             
          {
                
          // Do nothing, merely provides the trigger that the shutdown hook has been
                
          // executed so as to persist the store on shutdown.
             }

             
             
          public void MainApplication.persist()
             
          {
                
          try
                
          {
                   ObjectOutput out 
          = new ObjectOutputStream(
                         
          new FileOutputStream(this.accountsFile));
                   
                   Object[] objectsToStore 
          = this.accounts.toArray();
                   out.writeObject(objectsToStore);
                   out.flush();
                   out.close();
                }

                
          catch (Exception e)
                
          {
                   System.err.println(
          "Couldn't store accounts to " + this.accountsFile);
                }

             }

             
             
          public void MainApplication.restore()
             
          {
                
          // Check that the serialized accounts file exists
                if (this.accountsFile.exists() && this.accountsFile.canRead())
                
          {
                   
          try
                   
          {
                      ObjectInput input 
          = new ObjectInputStream(
                            
          new FileInputStream(this.accountsFile));
                      
                      Object[] objectsToRestore 
          = (Object[]) input.readObject();
                      
          for(int x = 0; x < objectsToRestore.length; x++)
                      
          {
                         
          this.accounts.add(objectsToRestore[x]);
                      }

                      
                      input.close();
                   }

                   
          catch (Exception e)
                   
          {
                      System.err.println(
          "Couldn't restore accounts due to a corrupt " + this.accountsFile + " file");
                      e.printStackTrace();
                   }

                }

             }

          }


                  persistStore(ObjectStore)切入點修改成捕獲Runnable的執(zhí)行,它在MyApplication類上強制執(zhí)行public void run()方法。然后,AccountPersistenceAspect把必要的run()方法實現(xiàn)添加到MainApplication類中,以滿足Runnable接口的需要,但是,這只會提供一個標記,而不需要任何實現(xiàn)。
                  增加Runnable接口和MainApplication類上的run()存根方法意味著:可以利用JVM把MainApplication注冊為關(guān)閉掛鉤,使得在整個應(yīng)用程序正常完成時將調(diào)用MainApplication.run()方法。在恢復(fù)ObjectStore(它是一個MainApplication類)時,通過執(zhí)行around(MainApplication)通知塊,來完成把MainApplication類注冊為關(guān)閉掛鉤的任務(wù)。
                  通過把MainApplication用作關(guān)閉掛鉤,當觸發(fā)關(guān)閉掛鉤時,persistStoreage(ObjectStore)切入點將觸發(fā)MainApplication對象的保持操作。因此,一旦干凈利索地關(guān)閉應(yīng)用程序,就會保持MainApplication中存儲的Account類的集合。

          四. 應(yīng)用模擬組件支持單元測試

                  創(chuàng)建組件所依賴的外部組件的模擬實現(xiàn)。創(chuàng)建一個方面,應(yīng)用模擬組件實現(xiàn)來代替真實的組件。單元測試完成時,使用單獨的AspectJ構(gòu)建配置文件裝換出測試文件,使得可以再次使用真實的實現(xiàn)。

                  以下為一種典型情況,MyComponent是要測試的組件,并且它具有與ThirdPartyComponentInterface的外部實現(xiàn)的依賴性。ThirdPartyComponentInterface的真實實現(xiàn)是通過調(diào)用工廠方法ThirdPartyFactory.getThirdPartyComponent()來獲得的。

          package com.aspectj;

          import com.thirdparty.ThirdPartyComponentFactory;
          import com.thirdparty.ThirdPartyComponentInterface;

          public class MyComponent implements MyComponentInterface
          {
              
          private ThirdPartyComponentInterface thirdPartyComponent;
              
              
          public MyComponent()
              
          {
                  
          this.thirdPartyComponent = ThirdPartyComponentFactory.getThirdPartyComponent();
                  System.out.println(
          "Component found " + thirdPartyComponent);
              }

              
              
          public void foo()
              
          {
                  System.out.println(
          "Inside MyComponent.foo()");
                  
          this.thirdPartyComponent.bar();
              }

          }


                  為了孤立地對MyComponent運行單元測試,將需要通過在測試中包括真實的外部組件,重寫ThirdPartyComponent實現(xiàn),從而不會混淆測試結(jié)果。一種策略是:手動應(yīng)用重寫真實組件實現(xiàn)的模擬組件。如:

          package test.com.aspectj;

          import com.aspectj.ThirdPartyComponentInterface;

          public class MockThirdPartyComponent implements ThirdPartyComponentInterface {

              
          /* (non-Javadoc)
               * @see com.thirdparty.ThirdPartyComponentInterface#bar()
               
          */

              
          public void bar() 
              
          {
                  System.out.println(
          "Inside MockThirdPartyComponent.bar()");

              }

          }


                  以上方法如果用在組件較多的接口,可能難以管理。使用面向方面的替代方法,可以創(chuàng)建一個方面,用于截獲ThirdPartyComponent的創(chuàng)建,并用模擬對象實現(xiàn)重寫返回的對象。

          package test.com.aspectj;

          import com.aspectj.*;

          public aspect MockThirdPartyComponentAspect 
          {
              
          public pointcut catchThirdPartyConstructor() : 
                 call(ThirdPartyComponentInterface ThirdPartyComponentFactory.
                       getThirdPartyComponent());
              
              Object around() : catchThirdPartyConstructor()
              
          {
                  
          return new MockThirdPartyComponent();
              }

          }


                  可以通過創(chuàng)建兩個不同的AspectJ構(gòu)建配置文件,來區(qū)分真實實現(xiàn)和模擬實現(xiàn)。


          posted on 2008-08-29 11:18 Brian 閱讀(341) 評論(0)  編輯  收藏 所屬分類: 《AspectJ Cookbook》讀書筆記

          公告


          導(dǎo)航

          <2008年8月>
          272829303112
          3456789
          10111213141516
          17181920212223
          24252627282930
          31123456

          統(tǒng)計

          常用鏈接

          留言簿(4)

          隨筆分類

          隨筆檔案

          收藏夾

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 桦南县| 常德市| 香河县| 铁岭县| 德昌县| 八宿县| 阳原县| 黔西| 隆子县| 白水县| 永登县| 海口市| 日喀则市| 平江县| 高邮市| 基隆市| 南乐县| 教育| 久治县| 准格尔旗| 德保县| 宣武区| 株洲县| 区。| 承德县| 青田县| 建始县| 开阳县| 怀安县| 建平县| 如东县| 蛟河市| 星子县| 池州市| 阳信县| 安远县| 北票市| 察隅县| 璧山县| 肥东县| 贵德县|