天天看點

Java Web之動态代理

動态代理通俗解釋:

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);
  }
}