Changes between Version 30 and Version 31 of waue/2011/spring


Ignore:
Timestamp:
Sep 7, 2011, 12:05:03 PM (13 years ago)
Author:
waue
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • waue/2011/spring

    v30 v31  
    778778}}}
    779779
    780 
     780 = spring aop =
     781
     782在一個服務的流程中插入與服務無關的邏輯(例如Logging、Security),這樣的邏輯稱為 Cross-cutting concerns,將 Crossing-cutting concerns 獨立出來為一個物件,這樣的特殊物件稱之為 Aspect,Aspect-oriented programming 著重在 Aspect 的設計及與應用程式的縫合(Weave)。
     783
     784
     785可以使用代理(Proxy)機制來解決這個問題,在這邊討論兩種代理方式:靜態代理(Static proxy)與動態代理(Dynamic proxy)。
     786
     787以下範例中,HelloProxy或是LogHandler,這樣的物件稱之為切面(Aspect)
     788
     789AOP中的Aspect所指的可以是像日誌等這類的動作或服務,
     790您將這些動作(Cross-cutting concerns)設計為通用、
     791不介入特定業務物件的一個職責清楚的Aspect物件,
     792這就是所謂的Aspect-oriented programming,縮寫名詞即為AOP。
     793
     794Aspect可以獨立於應用程式之外,在必要的時候,可以介入應用程式之中提供服務;
     795而不需要相關服務的時候,又可以將這些Aspect直接從應用程式中脫離,
     796而您的應用程式本身不需修改任何一行程式碼。
     797
     798
     799== 無 aop ==
     800
     801 * HelloSpeaker.java
     802
     803{{{
     804#!java
     805package onlyfun.caterpillar;
     806
     807import java.util.logging.*;
     808
     809public class HelloSpeaker {
     810    private Logger logger =
     811            Logger.getLogger(this.getClass().getName());
     812
     813    public void hello(String name) {
     814        // 方法執行開始時留下日誌
     815        logger.log(Level.INFO, "hello method starts....");
     816        // 程式主要功能
     817        System.out.println("Hello, " + name);
     818        // 方法執行完畢前留下日誌
     819        logger.log(Level.INFO, "hello method ends....");
     820    }
     821}
     822}}}
     823在HelloSpeaker類別中,當執行hello()方法時,您希望該方法執行開始與執行完畢時都能留下日誌,最簡單的作法就是如以上的程式設計,在方法執行的前後加上日誌動作,然而記錄的這幾行程式碼橫切入(Cross-cutting)HelloSpeaker類別中,對於 HelloSpeaker來說,日誌的這幾個動作並不屬於HelloSpeaker商務邏輯(顯示"Hello"等文字),這使得 HelloSpeaker增加了額外的職責
     824
     825  == 靜態代理 ==
     826
     827
     828 * IHello.java
     829
     830{{{
     831#!java
     832package onlyfun.caterpillar;
     833
     834public interface IHello {
     835    public void hello(String name);
     836}
     837}}}
     838
     839 * HelloSpeaker.java
     840
     841{{{
     842#!java
     843package onlyfun.caterpillar;
     844
     845public class HelloSpeaker implements IHello {
     846    public void hello(String name) {
     847        System.out.println("Hello, " + name);
     848    }
     849}
     850
     851 * HelloProxy.java
     852
     853{{{
     854#!java
     855
     856package onlyfun.caterpillar;
     857
     858import java.util.logging.*;
     859
     860public class HelloProxy implements IHello {
     861    private Logger logger =
     862            Logger.getLogger(this.getClass().getName());
     863   
     864    private IHello helloObject;
     865
     866    public HelloProxy(IHello helloObject) {
     867        this.helloObject = helloObject;
     868    }
     869
     870    public void hello(String name) {
     871        // 日誌服務
     872        log("hello method starts....");     
     873
     874        // 執行商務邏輯
     875        helloObject.hello(name);
     876       
     877        // 日誌服務
     878        log("hello method ends....");
     879    }
     880   
     881    private void log(String msg) {
     882        logger.log(Level.INFO, msg);
     883    }
     884}
     885}}}
     886
     887 * ProxyDemo.java
     888
     889{{{
     890#!java
     891package onlyfun.caterpillar;
     892
     893public class ProxyDemo {
     894    public static void main(String[] args) {
     895        IHello proxy =
     896            new HelloProxy(new HelloSpeaker());
     897        proxy.hello("Justin");
     898    }
     899}
     900
     901
     902== 動態 aop ==
     903
     904jdk本身即有可協助開發動態代理功能的API等相關類別,您不必為特定物件與方法撰寫特定的代理物件,使用動態代理,可以使得一個處理者(Handler)服務於各個物件
     905
     906主要的概念是使用Proxy.newProxyInstance()靜態方法建立一個代理物件,建立代理物件時必須告知所要代理的介面,
     907
     908操作所建立的代理物件,在每次操作時會呼叫InvocationHandler的invoke()方法,invoke()方法會傳入被代理物件的方法名稱與執行參數,
     909
     910實際上要執行的方法交由method.invoke(),您在method.invoke()前後加上記錄動作,method.invoke()傳回的物件是實際方法執行過後的回傳結果。
     911
     912使用LogHandler的bind()方法來綁定被代理物件
     913
     914
     915 * LogHandler.java
     916
     917{{{
     918#!java
     919package onlyfun.caterpillar;
     920
     921import java.util.logging.*;
     922import java.lang.reflect.*;
     923
     924public class LogHandler implements InvocationHandler {
     925    private Logger logger =
     926            Logger.getLogger(this.getClass().getName());
     927   
     928    private Object delegate;
     929
     930    public Object bind(Object delegate) {
     931        this.delegate = delegate;
     932        return Proxy.newProxyInstance(
     933                           delegate.getClass().getClassLoader(),
     934                           delegate.getClass().getInterfaces(),
     935                           this);
     936    }
     937
     938    public Object invoke(Object proxy, Method method,
     939                         Object[] args) throws Throwable {
     940        Object result = null;
     941       
     942        try {
     943            log("method starts..." + method);
     944           
     945            result = method.invoke(delegate, args);
     946           
     947            logger.log(Level.INFO, "method ends..." + method);
     948        } catch (Exception e){
     949            log(e.toString());
     950        }
     951       
     952        return result;
     953    }
     954   
     955    private void log(String message) {
     956        logger.log(Level.INFO, message);
     957    }
     958}
     959
     960}}}
     961
     962 * (同前) IHello.java
     963
     964{{{
     965#!java
     966package onlyfun.caterpillar;
     967
     968public interface IHello {
     969    public void hello(String name);
     970}
     971}}}
     972
     973 * (同前) HelloSpeaker.java
     974
     975{{{
     976#!java
     977package onlyfun.caterpillar;
     978
     979public class HelloSpeaker implements IHello {
     980    public void hello(String name) {
     981        System.out.println("Hello, " + name);
     982    }
     983}
     984}}}
     985
     986 * ProxyDemo.java
     987
     988{{{
     989#!java
     990package onlyfun.caterpillar;
     991
     992public class ProxyDemo {
     993    public static void main(String[] args) {
     994        LogHandler logHandler  = new LogHandler();
     995       
     996        IHello helloProxy =
     997                (IHello) logHandler.bind(new HelloSpeaker());
     998        helloProxy.hello("Justin");
     999    }
     1000}
     1001}}}
     1002
     1003== 靜態與動態 ==
     1004
     1005 動態aop 與 靜態 aop 中,(主要介面) I_Hello.java 與 (主要功能)HelloSpeaker.java 內容相同
     1006
     1007 B. 靜態aop
     1008 
     1009 主要不同為: HelloProxy.java
     1010 
     1011 main 呼叫: IHello proxy = new HelloProxy(new HelloSpeaker());
     1012 
     1013
     1014 
     1015 C. 動態aop
     1016 
     1017 主要不同為: LogHandler.java
     1018 
     1019 main 呼叫:IHello helloProxy = (IHello) logHandler.bind(new HelloSpeaker());
     1020 
     1021 其中 I_Hello  與 HelloSpeaker 相同於 動態aop 方法
    7811022
    7821023 = eclipse 開發環境 =