java 動態代理初步理解

別看小弟工做了一段時間,但對代理的理解一直不行,甚至根本就是懵逼狀態。而後今天忽然開竅。就趕忙記下一些理解。html

java 的動態代理主要使用java.lang.reflect.Proxy。若是本身建立一個代理類的話,就須要自定義一個class實現invocationHandler,並重寫invoke方法才行,這樣自定義的class 才能在invoke中建立代理並調用被代理的方法。java

而invocationHandler 接口只有一個invoke方法。就是給代理對象處理業務用的。spring

下面在說下Proxy,該對象主要負責建立代理,建立代理對象的源碼以下:數組

@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
    throws IllegalArgumentException
{
    Objects.requireNonNull(h);

    final Class<?>[] intfs = interfaces.clone();
    final SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
    }

    /*
     * Look up or generate the designated proxy class.
     */
    Class<?> cl = getProxyClass0(loader, intfs);

    /*
     * Invoke its constructor with the designated invocation handler.
     */
    try {
        if (sm != null) {
            checkNewProxyPermission(Reflection.getCallerClass(), cl);
        }

        final Constructor<?> cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
        if (!Modifier.isPublic(cl.getModifiers())) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    cons.setAccessible(true);
                    return null;
                }
            });
        }
        return cons.newInstance(new Object[]{h});
    } catch (IllegalAccessException|InstantiationException e) {
        throw new InternalError(e.toString(), e);
    } catch (InvocationTargetException e) {
        Throwable t = e.getCause();
        if (t instanceof RuntimeException) {
            throw (RuntimeException) t;
        } else {
            throw new InternalError(t.toString(), t);
        }
    } catch (NoSuchMethodException e) {
        throw new InternalError(e.toString(), e);
    }
}

三個參數分別是:緩存

一、代理對象的類加載器,經過調用getParent 發現是sun.misc.Launcher$ExtClassLoader@41629346框架

證實是APPClassLoader,系統級的類加載器jvm

二、是一個class對象接口數組,該數組都是多態形式的,你能夠傳實現類的實體對象,但該方法返回的必定是對應多態的引用對象ide

三、代理類對象相關聯的invocationHandler,是invocationHandler對象測試

好了。瞭解完參數。該怎麼建立一個對象的動態代理對象呢?ui

步驟以下:

一、建立一個實體接口

package com.wisely.proxy;

/**
 * DES:代理對象實體bean
 * Created by Reynole-白
 * Date: 2017/8/26 16:27
 */
public interface Person {

    void sing(String songName);

    void dance(String danceName);

}

二、建立其實現

package com.wisely.proxy;

/**
 * DES:
 * Created by Reynole-白
 * Date: 2017/8/26 16:29
 */
public class LiuDeHua implements Person {

    public LiuDeHua(){
        System.out.println("我是華仔的無參構造器");
    }

    @Override
    public void sing(String songName) {
        System.out.println("華仔唱:" + songName);
    }

    @Override
    public void dance(String danceName) {
        System.out.println("華仔跳:" + danceName);
    }
}

三、建立與動態代理類相關的invocationHandler類,invoke的三個參數的定義在方法註釋中有體現。

package com.wisely.proxy;

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

/**
 * DES:代理對象 要牛逼的
 * Created by Reynole-白
 * Date: 2017/8/26 16:30
 */
public class LiuDeHuaProxy implements InvocationHandler {

    private Person pp = new LiuDeHua();

    /**
     * 代理對象執行的invoke方法,若是想讓代理對象作些邏輯操做,能夠在invoke中進行編碼
     * @param proxy   表示代理對象,這個對象纔是真正的動態代理對象
     * @param method  代理對象當前執行的方法
     * @param args    方法參數
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName = method.getName();

        Object obj = args[0];
        if(obj instanceof String){
            System.out.println("傳入的參數是string類型的");
        }

        //代理對象要作的事情
        if("sing".equals(methodName)){
            System.out.println("我是華仔的代理對象,找他唱歌先給錢~!!!" +  args[0]);
            System.out.println("已收到你的10K RMB,通知華仔……");
            pp.sing(args[0].toString());
        }else if("dance".equals(methodName)){
            System.out.println("我是華仔的代理對象,找他跳舞,先經過我這關!!~~" +  args[0]);
            System.out.println("你已通關,通知華仔……");
            pp.dance(args[0].toString());
        }

        return null;
    }
}

測試類:若是不是多態形式接收代理生成的Object 那麼編譯不經過

package com.wisely.proxy;

import java.lang.reflect.Proxy;

/**
 * DES: 代理測試類
 * Created by Reynole-白
 * Date: 2017/8/26 16:38
 */
public class ProxyTestMain {

    public static void main(String[] args) {

        Person p = new LiuDeHua();//被代理的對象
        Person p2 = new JavaFatcher();
        /**
         * 這裏代理對象的生成其實能夠寫到代理對象中,以匿名內部類的形式
         * 這裏要注意,代理對象 必需要以多態的形式定義
         */
        ClassLoader cl = LiuDeHuaProxy.class.getClassLoader();
        System.out.println(cl.getParent());
        Person personProxy = (Person) Proxy.newProxyInstance(LiuDeHuaProxy.class.getClassLoader(),p.getClass().getInterfaces(),
                new LiuDeHuaProxy());

        personProxy.sing("冰雨");
        personProxy.dance("迪斯科");

    }

}

運行結果以下:

我是華仔的無參構造器
sun.misc.Launcher$ExtClassLoader@41629346
我是華仔的無參構造器
傳入的參數是string類型的
我是華仔的代理對象,找他唱歌先給錢~!!!冰雨
已收到你的10K RMB,通知華仔……
華仔唱:冰雨
傳入的參數是string類型的
我是華仔的代理對象,找他跳舞,先經過我這關!!~~迪斯科
你已通關,通知華仔……
華仔跳:迪斯科

Process finished with exit code 0

-------------------------------------------------------------------------------------------------

以上例子,參考了http://blog.csdn.net/pangqiandou/article/details/52964066 這篇博客的例子

其使用場景 就是在大多數框架設計以及 封裝設計的時候使用。通常搬磚的像我這樣的猿尚未接觸到。但若是想看spring這類框架的源碼的話尤爲是AOP ,是須要了解其動態代理的。

以上,有不對的地方,請個位大大海涵,並友情指正。拜謝!!!

下一步好好研究一下反射。

=============================================================

補充。由於小弟近期在看dubbo的源碼。接觸到了dubbo 的代理模式。就複習了jdk 的接口類型的動態代理。

感受以前理解的東西有些出入。特此補充一下:

一、實現InvocationHandler的類  是一箇中介類。。負責關聯動態生成的代理,並調用被代理對象的方法。

二、代理類在日誌輸出上都有一些特殊的標記,如:

$Proxy0

0表明第幾個。多個會依次累加。

三、真正建立動態代理對象的是Proxy 的newProxyInstance方法 中的Class<?> cl = getProxyClass0(loader, intfs);這句。

四、動態代理對象 在建立時,是存放在jvm緩存中的class文件。反編譯後爲:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import proxy.Person;

public final class $Proxy0 extends Proxy implements Person
{
  private static Method m1;
  private static Method m2;
  private static Method m3;
  private static Method m0;
  
  /**
  *注意這裏是生成代理類的構造方法,方法參數爲InvocationHandler類型,看到這,是否是就有點明白
  *爲什麼代理對象調用方法都是執行InvocationHandler中的invoke方法,而InvocationHandler又持有一個
  *被代理對象的實例,不由會想難道是....? 沒錯,就是你想的那樣。
  *
  *super(paramInvocationHandler),是調用父類Proxy的構造方法。
  *父類持有:protected InvocationHandler h;
  *Proxy構造方法:
  *    protected Proxy(InvocationHandler h) {
  *         Objects.requireNonNull(h);
  *         this.h = h;
  *     }
  *
  */
  public $Proxy0(InvocationHandler paramInvocationHandler)
    throws 
  {
    super(paramInvocationHandler);
  }
  
  //這個靜態塊原本是在最後的,我把它拿到前面來,方便描述
   static
  {
    try
    {
      //看看這兒靜態塊兒裏面有什麼,是否是找到了giveMoney方法。請記住giveMoney經過反射獲得的名字m3,其餘的先無論
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      m3 = Class.forName("proxy.Person").getMethod("giveMoney", new Class[0]);
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
  }
 
  /**
  * 
  *這裏調用代理對象的giveMoney方法,直接就調用了InvocationHandler中的invoke方法,並把m3傳了進去。
  *this.h.invoke(this, m3, null);這裏簡單,明瞭。
  *來,再想一想,代理對象持有一個InvocationHandler對象,InvocationHandler對象持有一個被代理的對象,
  *再聯繫到InvacationHandler中的invoke方法。嗯,就是這樣。
  */
  public final void giveMoney()
    throws 
  {
    try
    {
      this.h.invoke(this, m3, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  //注意,這裏爲了節省篇幅,省去了toString,hashCode、equals方法的內容。原理和giveMoney方法一毛同樣。

}

仔細觀察 反編譯的 動態代理class 就會明白。爲什麼 實現invocationHandler 類中invoke 方法的三個參數哪裏來的對象。怎麼用的。

以上補充 參考自:https://www.cnblogs.com/gonjan-blog/p/6685611.html

相關文章
相關標籤/搜索