JavaWeb之動態代理

yungfan發表於2017-01-05

動態代理通俗解釋:

A介面有c方法,類B實現A介面,原本應該是執行B類中的c方法,可現在不這樣做,可以先宣告產生B類的代理類B`,由它來冒充B類的“兄弟”並“實現”A介面, 對外界來說B`應該也有c方法,可當真正呼叫它的時候, 它會去執行與它關聯InvocationHandler的invoke()方法, 在這個方法裡面你可以做很多事情。

Java怎樣實現動態代理呢

第一步,我們要有一個介面,還要有一個介面的實現類,而這個實現類就是我們要代理的類。

public interface Subject
{
    public void request();
}

public class RealSubject implements Subject
{
    public void request()
    {
        System.out.println("From real subject!");
    }

}

第二步,我們要自己寫一個代理類,它的特點是實現了InvocationHandler介面, 因為代理類的例項在呼叫實現類的方法的時候,不是去呼叫真正的實現類的這個方法, 而是呼叫代理類的invoke()方法,在這個方法中才呼叫真正的實現類的方法。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * 該代理類的內部屬性是Object型別,實際使用的時候通過該類的構造方法傳遞進來一個物件
 * 此外,該類還實現了invoke方法,該方法中的method.invoke其實就是呼叫被代理物件的將要
 * 執行的方法,方法引數是sub,表示該方法從屬於sub,通過動態代理類,我們可以在執行真實物件的方法前後
 * 加入自己的一些額外方法,這裡在方法呼叫前後列印一句話。
 *
 */

public class DynamicSubject implements InvocationHandler
{
    private Object sub;
    
    public DynamicSubject(Object obj)
    {
        this.sub = obj;
    }
    
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable
    {
        System.out.println("before calling: " + method);
        
        method.invoke(sub, args);
        
        System.out.println(args == null);
        
        System.out.println("after calling: " + method);
        
        return null;
    }   
    
}

上述方法體中method.invoke(owner, args)的解釋:執行該method.invoke方法的引數是執行這個方法的物件owner,引數陣列args,可以這麼理解:owner物件中帶有引數args的method方法。返回值是Object,也就是該方法的返回值。

第三步,客戶端要用代理類的例項去呼叫實現類的方法。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class Client
{
    public static void main(String[] args)
    {
        RealSubject realSubject = new RealSubject();

        InvocationHandler handler = new DynamicSubject(realSubject);

        Class<?> classType = handler.getClass();

        // newProxyInstance()動態生成一個類並載入到記憶體
        // 載入到記憶體要使用載入器,第一個引數是一個類載入器
        Subject subject = (Subject) Proxy.newProxyInstance(classType
                .getClassLoader(), realSubject.getClass().getInterfaces(),
                handler);

        subject.request();

        System.out.println(subject.getClass());

    }

}

對第三步的解釋

主要是以下程式碼:

Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

該方法主要做了如下工作:

  • 根據引數loader和interfaces呼叫方法 getProxyClass(loader, interfaces) 建立代理類$Proxy0,該代理類實現了預先定義的介面(如上面的Subject),並繼承了Proxy類。
public final class $Proxy0 extends Proxy implements Subject
  • 例項化$Proxy0(建立代理物件)並在構造方法中把 InvocationHandler(這裡handler 是它的例項)傳過去

  • $Proxy0呼叫父類Proxy的構造器,為InvocationHandler 賦值:

public $Proxy0(InvocationHandler invocationhandler)
{
  super(invocationhandler);
}
========================================================
class Proxy
{
   protected InvocationHandler h;
   protected Proxy(InvocationHandler h) 
   {
         this.h = h;
   }
}
  • 將這個$Proxy0類強制轉型成介面型別,當執行介面中的方法時(如上文強轉成Subject後呼叫request()方法),就呼叫了$Proxy0類中實現的介面方法,在該方法中會呼叫父類Proxy中的invoke()方法,即InvocationHandler.invoke(),達到做一些其他工作的效果。
public final void request()
{
  try
  {
      //m是通過反射得到的方法名 Method型別
      super.h.invoke(this, m, null);
      return;
  }
  catch (Error e)
  {

  }
  catch (Throwable throwable)
  {
    throw new UndeclaredThrowableException(throwable);
  }
}


相關文章