代理模式及jdk動態代理原理

代理模式 :爲其它對象提供代理,以控制對這個對象的訪問。java

代理模式的特徵:代理類(proxyClass)與委託類(realClass)有一樣的接口,代理類主要負責爲委託類預處理消息、過濾消息、把消息轉發給委託類(調用realClass的方法,實現代理的功能),以及過後處理消息等。代理類與委託類之間一般會存在關聯關係,一個代理類的對象與一個委託類的對象關聯,代理類的對象自己並不真正實現服務,而是經過調用委託類的對象的相關方法,來提供特定的服務。 數組

做用:主要用來作方法的加強,讓你能夠在不修改源碼的狀況下,加強一些方法,在方法執行先後作任何你想作的事情(甚至根本不去執行這個方法),由於在InvocationHandler的invoke方法中,你能夠直接獲取正在調用方法對應的Method對象,具體應用的話,好比能夠添加調用日誌,作事務控制等ide

 動態代理結論:Proxy.newProxyInstance根據classloader和接口數組,生成一個$Proxy0代理類,並將InvocationHandler的實現類作爲構造函數參數傳遞給$Proxy0代理類,代理類$Proxy0調用接口的方法,會在方法中調用InvocationHandler的實現類中的invoke()方法。invoke()方法中,經過反射調用委託類(realClass)的實際方法,完成了整個代理操做。函數

 

1.爲了更好的理解代理模式,先理解靜態代理測試

模擬支付寶支付,你們在淘寶買過東西都知道,買家付款後,錢放在支付寶裏,不會馬上給商家,待用戶確認收貨後,纔會支付給商家,這裏支付寶就至關於代理用戶付款給商家。this

 

支付接口spa

package com.proxy;

public interface Pay {
    public boolean pay();
}

 

委託類實現了支付接口代理

package com.proxy;

public class Customer implements Pay{
    @Override
    public boolean pay() {
        System.out.println("網上購物使用支付寶結帳付款");
        return true;
    }
}

 

代理類也要實現pay接口,包含委託類對象(關聯關係)日誌

package com.proxy;

public class AliPay implements Pay{
    private Customer customer = null;
    public AliPay(Customer customer){
        this.customer = customer;
    }
    
    @Override
    public boolean pay() {
        if(customer.pay()){
            System.out.println("用戶收到貨物,支付包支付給商家完成");
        }
        return customer.pay();
    }
}

 

測試code

package com.proxy;

public class TestProxy {
    public static void main(String[] args) {
        Customer customer = new Customer();
        AliPay aliPay = new AliPay(customer);
        if(aliPay.pay()){
            System.out.println("整個購物流程完成!");
        }else{
            System.out.println("付款失敗!");
        }
    }
}

 

結果:

網上購物使用支付寶結帳付款
用戶收到貨物,支付包支付給商家完成
整個購物流程完成!

 

使用靜態代理髮現,代理類只能爲一個接口服務,這樣會產生許多代理類。這樣一來程序開發中必然會產生過多的代理,並且,全部的代理操做除了調用的方法不同以外,其餘的操做都同樣,則此時確定是重複代碼。解決這一問題最好的作法是能夠經過一個代理類完成所有的代理功能,那麼此時就必須使用動態代理完成。 

 

動態代理:

代理類

package com.proxy;

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

public class ProxyPay implements InvocationHandler {
    private Object target;  
    
    /**
     * @param proxy  代理的實例proxy instance  
     * @param method 代理的實例proxy instance調用接口的方法
     * @param args   調用實際類方法的參數數組,沒有參數爲null,基本類型會轉成封裝類
     * @return
     * @date 2016-4-7
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        Object pay =  method.invoke(target, args);
        if((Boolean) pay){
            System.out.println("用戶收到貨物,支付包支付給商家完成");
        }
        return pay;
    }

    
    /**
     * loader : 類加載器
     * interfaces : 代理類實現的接口
     * h :調用方法
     * 
     * public static Object newProxyInstance(ClassLoader loader,
                      Class<?>[] interfaces,
                      InvocationHandler h)
     */
    public Object bind(Object target) {  
        this.target = target;  
        //取得代理對象  
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),  
                target.getClass().getInterfaces(), this);   
    }  

}

動態代理測試

package com.proxy;

public class TestDynamicProxy {
    public static void main(String[] args) {
        Customer customer = new Customer();
        ProxyPay proxyPay = new ProxyPay();
        Pay pay =  (Pay) proxyPay.bind(customer);
        if(pay.pay()){
            System.out.println("整個購物流程完成!");
        }else{
            System.out.println("付款失敗!");
        }
    }
}

結果:

網上購物使用支付寶結帳付款
用戶收到貨物,支付包支付給商家完成
整個購物流程完成!

 

雖然完成了動態代理的代碼操做,可是對於整個流程仍是有些疑惑,是怎麼調用代理類中的invoke方法。

因而看源代碼發現

public static Object newProxyInstance(ClassLoader loader,
                      Class<?>[] interfaces,
                      InvocationHandler h)
    throws IllegalArgumentException
    {
    if (h == null) {
        throw new NullPointerException();
    }

    /*
     * Look up or generate the designated proxy class.
     */
    Class cl = getProxyClass(loader, interfaces);

    /*
     * Invoke its constructor with the designated invocation handler.
     */
    try {
        Constructor cons = cl.getConstructor(constructorParams);
        return (Object) cons.newInstance(new Object[] { h });
    } catch (NoSuchMethodException e) {
        throw new InternalError(e.toString());
    } catch (IllegalAccessException e) {
        throw new InternalError(e.toString());
    } catch (InstantiationException e) {
        throw new InternalError(e.toString());
    } catch (InvocationTargetException e) {
        throw new InternalError(e.toString());
    }
    }
/*
* Returns the <code>java.lang.Class</code> object for a proxy class
* given a class loader and an array of interfaces. The proxy class
* will be defined by the specified class loader and will implement
* all of the supplied interfaces. If a proxy class for the same
* permutation of interfaces has already been defined by the class
* loader, then the existing proxy class will be returned; otherwise,
* a proxy class for those interfaces will be generated dynamically
* and defined by the class loader.
*/
getProxyClass方法就是根據給定的classload和interface生成代理類,若是存在,就返回存在的代理類。
cons.newInstance(new Object[] { h })能夠看出代理類中的構造函數是傳進去的InvocationHandler對象,會調用InvocationHandler實現類的invoke方法


package com.proxy;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import sun.misc.ProxyGenerator;
public class ProxyPay implements InvocationHandler {
    private Object target;  
    private static boolean flag = true;
    /**
     * @param proxy  代理的實例proxy instance  
     * @param method 代理的實例proxy instance調用接口的方法
     * @param args   調用實際類方法的參數數組,沒有參數爲null,基本類型會轉成封裝類
     * @return
     * @date 2016-4-7
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        /**
         * Generate a proxy class given a name and a list of proxy interfaces.
         */
        String name = "$Proxy0.class";
        byte[] bytes = ProxyGenerator.generateProxyClass(name, new Class[]{Pay.class});
        FileOutputStream os = new FileOutputStream(new File(ProxyPay.class.getClassLoader().getResource("").getPath(),name));
        os.write(bytes);
        
        System.out.println(proxy.getClass().getName());
        Object pay =  method.invoke(target, args);
        if((Boolean) pay){
            System.out.println("用戶收到貨物,支付包支付給商家完成");
        }
        if(flag){
            flag = false;
            System.out.println("---------------");
            System.out.println(((Pay) proxy.getClass().getConstructor(InvocationHandler.class).newInstance(this)).pay());
        }
        return pay;
    }

    
    /**
     * loader : 類加載器
     * interfaces : 代理類實現的接口
     * h :調用方法
     * 
     * public static Object newProxyInstance(ClassLoader loader,
                      Class<?>[] interfaces,
                      InvocationHandler h)
     */
    public Object bind(Object target) {  
        this.target = target;  
        //取得代理對象  
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),  
                target.getClass().getInterfaces(), this);   
    }  

}
 

結果:

$Proxy0
網上購物使用支付寶結帳付款
用戶收到貨物,支付包支付給商家完成
---------------
$Proxy0
網上購物使用支付寶結帳付款
用戶收到貨物,支付包支付給商家完成
true
整個購物流程完成!

 

經過proxy.getClass().getName()能夠看到proxy代理類是$Proxy0,在內存中存儲,經過反射獲得此類的構造函數傳入了InvocationHandler,調用接口的pay()方法,
發現會調用InvocationHandler實現類的invoke方法,這也證實了上面說的是正確的。使用ProxyGenerator.generateProxyClass(name, new Class[]{Pay.class});生成了$Proxy0的源碼,
經過下面的源碼pay()方法的return ((Boolean)this.h.invoke(this, m3, null)).booleanValue();能夠看到會調用InvocationHandler實現類的invoke方法。
package $Proxy0;

import com.proxy.Pay;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class class extends Proxy
  implements Pay
{
  private static Method m1;
  private static Method m3;
  private static Method m0;
  private static Method m2;

  public class(InvocationHandler paramInvocationHandler)
    throws 
  {
    super(paramInvocationHandler);
  }

  public final boolean equals(Object paramObject)
    throws 
  {
    try
    {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final boolean pay()
    throws 
  {
    try
    {
      return ((Boolean)this.h.invoke(this, m3, null)).booleanValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final int hashCode()
    throws 
  {
    try
    {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final String toString()
    throws 
  {
    try
    {
      return (String)this.h.invoke(this, m2, null);
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  static
  {
    try
    {
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m3 = Class.forName("com.proxy.Pay").getMethod("pay", new Class[0]);
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
  }
}

 

結論:Proxy.newProxyInstance根據classloader和接口數組,生成一個$Proxy0代理類,並將InvocationHandler的實現類作爲構造函數參數傳遞給$Proxy0代理類,代理類$Proxy0調用接口的方法,會在方法中調用InvocationHandler的實現類中的invoke()方法。invoke()方法中,經過反射調用委託類(realClass)的實際方法,完成了整個代理操做。

相關文章
相關標籤/搜索