java靜態代理和動態代理

JAVA的靜態代理與動態代理比較
一、概念
    代理模式是常用的Java 設計模式,它的特徵是代理類與委託類有同樣的接口,代理類主要負責爲委託類預處理消息、過濾消息、把消息轉發給委託類,以及事後處理消息等。代理類與委託類之間通常會存在關聯關係,一個代理類的對象與一個委託類的對象關聯,代理類的對象本身並不真正實現服務,而是通過調用委託類的對象的相關方法,來提供特定的服務。按照代理類的創建時期,代理類可分爲兩種。
靜態代理類:
     由程序員創建或由特定工具自動生成源代碼,再對其編譯。在程序運行前,代理類的.class文件就已經存在了。動態代理類:在程序運行時,運用反射機制動態創建而成。
二、靜態代理類
     如下, HelloServiceProxy 類是代理類,HelloServiceImpl類是委託類,這兩個類都實現了HelloService接口。其中HelloServiceImpl類是HelloService接口的真正實現者,而HelloServiceProxy類是通過調用HelloServiceImpl 類的相關方法來提供特定服務的。HelloServiceProxy類的echo()方法和getTime()方法會分別調用被代理的HelloServiceImpl 對象的echo()方法和getTime()方法,並且在方法調用前後都會執行一些簡單的打印操作。

由此可見,代理類可以爲委託類預處理消息、把消息轉發給委託類和事後處理消息等。

例程1 HelloService.java

import java.util.Date;
public interface HelloService{
   public String echo(String msg);
   public Date getTime();
}

例程2 HelloServiceImpl.java

import java.util.Date;
public class HelloServiceImpl implements HelloService{
   public String echo(String msg){
     return "echo:"+msg;
   }
   public Date getTime(){
    return new Date();
   }
}

例程3 HelloServiceProxy.java

import java.util.Date;
public class HelloServiceProxy implements HelloService{
   private HelloService helloService; //表示被代理的HelloService 實例
   public HelloServiceProxy(HelloService helloService){
     this.helloService=helloService;
   }
   public void setHelloServiceProxy(HelloService helloService){
      this.helloService=helloService;
   }
   public String echo(String msg){
     System.out.println("before calling echo()"); //預處理
     String result=helloService.echo(msg); //調用被代理的HelloService 實例的echo()方法
     System.out.println("after calling echo()"); //事後處理
     return result;
   }
   public Date getTime(){
     System.out.println("before calling getTime()"); //預處理
     Date date=helloService.getTime(); //調用被代理的HelloService 實例的getTime()方法
     System.out.println("after calling getTime()"); //事後處理
     return date;
    }
}


在Client1 類的main()方法中,先創建了一個HelloServiceImpl對象,又創建了一個HelloServiceProxy對象,最後調用HelloServiceProxy對象的echo()方法。

package proxy;
public class Client1{
   public static void main(String args[]){
    HelloService helloService=new HelloServiceImpl();
    HelloService helloServiceProxy=new HelloServiceProxy(helloService);
    System.out.println(helloServiceProxy.echo("hello"));
   }
}


運行Client1 類,打印結果如下:
before calling echo()
after calling echo()
echo:hello
例程3 的HelloServiceProxy 類的源代碼是由程序員編寫的,在程序運行前,它的.class文件就已經存在了,這種代理類稱爲靜態代理類。

三、動態代理類
      與靜態代理類對照的是動態代理類,動態代理類的字節碼在程序運行時由Java反射機制動態生成,無需程序員手工編寫它的源代碼。動態代理類不僅簡化了編程工作,而且提高了軟件系統的可擴展性,因爲Java 反射機制可以生成任意類型的動態代理類。java.lang.reflect 包中的Proxy類和InvocationHandler 接口提供了生成動態代理類的能力。

Proxy類提供了創建動態代理類及其實例的靜態方法。
(1)getProxyClass()靜態方法負責創建動態代理類,它的完整定義如下:

public static Class<?> getProxyClass(ClassLoader loader, Class<?>[] interfaces) throws IllegalArgumentException

   參數loader 指定動態代理類的類加載器,參數interfaces 指定動態代理類需要實現的所有接口。

(2)newProxyInstance()靜態方法負責創建動態代理類的實例,它的完整定義如下:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler) throws
      IllegalArgumentException

    參數loader 指定動態代理類的類加載器,參數interfaces 指定動態代理類需要實現的所有接口,參數handler 指定與動態代理類關聯的 InvocationHandler 對象。

以下兩種方式都創建了實現Foo接口的動態代理類的實例:
/**** 方式一 ****/
//創建InvocationHandler對象
InvocationHandler handler = new MyInvocationHandler(...);

//創建動態代理類
Class proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), new Class[] { Foo.class });

//創建動態代理類的實例
Foo foo = (Foo) proxyClass.getConstructor(new Class[] { InvocationHandler.class }).
    newInstance(new Object[] { handler });

/**** 方式二 ****/
//創建InvocationHandler對象
InvocationHandler handler = new MyInvocationHandler(...);

//直接創建動態代理類的實例
Foo foo = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),new Class[] { Foo.class }, handler);

由Proxy類的靜態方法創建的動態代理類具有以下特點:
   動態代理類是public、final和非抽象類型的;
   動態代理類繼承了java.lang.reflect.Proxy類;
   動態代理類的名字以“$Proxy”開頭;
   動態代理類實現getProxyClass()和newProxyInstance()方法中參數interfaces指定的所有接口;

Proxy 類的isProxyClass(Class<?> cl)靜態方法可用來判斷參數指定的類是否爲動態代理類。只有通過Proxy類創建的類纔是動態代理類;

動態代理類都具有一個public 類型的構造方法,該構造方法有一個InvocationHandler 類型的參數。

由Proxy類的靜態方法創建的動態代理類的實例具有以下特點:
1. 假定變量foo 是一個動態代理類的實例,並且這個動態代理類實現了Foo 接口,那麼“foo instanceof Foo”的值爲true。把變量foo強制轉換爲Foo類型是合法的:
(Foo) foo //合法

2.每個動態代理類實例都和一個InvocationHandler 實例關聯。Proxy 類的getInvocationHandler(Object proxy)靜態方法返回與參數proxy指定的代理類實例所關聯的InvocationHandler 對象。

3.假定Foo接口有一個amethod()方法,那麼當程序調用動態代理類實例foo的amethod()方法時,該方法會調用與它關聯的InvocationHandler 對象的invoke()方法。

InvocationHandler 接口爲方法調用接口,它聲明瞭負責調用任意一個方法的invoke()方法:
Object invoke(Object proxy,Method method,Object[] args) throws Throwable

參數proxy指定動態代理類實例,參數method指定被調用的方法,參數args 指定向被調用方法傳遞的參數,invoke()方法的返回值表示被調用方法的返回值。

四、最後看一個實例:
     HelloServiceProxyFactory 類的getHelloServiceProxy()靜態方法負責創建實現了HelloService接口的動態代理類的實例。
例程5 HelloServiceProxyFactory.java

import java.lang.reflect.*;
public class HelloServiceProxyFactory {
   /** 創建一個實現了HelloService 接口的動態代理類的實例
    * 參數helloService 引用被代理的HelloService 實例
    */
   public static HelloService getHelloServiceProxy(final HelloService helloService){
   //創建一個實現了InvocationHandler接口的匿名類的實例
   InvocationHandler handler=new InvocationHandler(){
      public Object invoke(Object proxy,Method method,Object args[])throws Exception{
        System.out.println("before calling "+method); //預處理
        Object result=method.invoke(helloService,args);
       //調用被代理的HelloService 實例的方法
        System.out.println("after calling "+method); //事後處理
        return result;
       }
     };
    Class classType=HelloService.class;
    return (HelloService)Proxy.newProxyInstance(classType.getClassLoader(),
        new Class[]{classType},
        handler);
    }
}


如下所示的Client2 類先創建了一個HelloServiceImpl 實例,然後創建了一個動態代理類實例helloServiceProxy,最後調用動態代理類實例的echo()方法。

例程6 Client2.java

public class Client2{
   public static void main(String args[]){
    HelloService helloService=new HelloServiceImpl();
    HelloService helloServiceProxy=HelloServiceProxyFactory.getHelloServiceProxy(helloService);
    System.out.println("動態代理類的名字爲"+helloServiceProxy.getClass().getName());
    System.out.println(helloServiceProxy.echo("Hello"));
   }
}


運行Client2,打印結果如下:
動態代理類的名字爲$Proxy0
before calling public abstract java.lang.String proxy.HelloService.echo(java.lang.String)
after calling public abstract java.lang.String proxy.HelloService.echo(java.lang.String)
echo:Hello
從結果看出,動態代理類的名字爲$Proxy0。

發佈了21 篇原創文章 · 獲贊 2 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章