JDK動態代理與CGLib動態代理

JDK動態代理

先寫一個JDK動態代理,一個演員需要有經紀人,我們把演員當成被代理類,經紀人當成代理類

演員類Actor.java:

package com.lw.designpattern.proxy.dynamic;

public class Actor {
    public void act() {
        System.out.println("某毯星戛納走紅毯");
    }
}

調用處理類ActorInvocation.java,爲什麼要設計這麼一個類,後面講

package com.lw.designpattern.proxy.dynamic;

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

public class ActorInvocation<T> implements InvocationHandler {

    T target;

    public ActorInvocation(T target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("經紀人準備");
        method.invoke(this.target, args);
        System.out.println("經紀人善後");
        return null;
    }
}

測試類:

package com.lw.designpattern.proxy.dynamic;

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

public class DynamicProxyTest {

    public static void main(String[] args) {
        Actor actor = new Actor();
        InvocationHandler actorHandler = new ActorInvocation<Actor>(actor);
        Actor actorProxy = (Actor) Proxy.newProxyInstance(Actor.class.getClassLoader(), new Class<?>[] { Actor.class }, actorHandler);

        actorProxy.act();
    }
}

測試結果: 

Exception in thread "main" java.lang.IllegalArgumentException: com.lw.designpattern.proxy.dynamic.Actor is not an interface
	at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:590)
	at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:557)
	at java.lang.reflect.WeakCache$Factory.get(WeakCache.java:230)
	at java.lang.reflect.WeakCache.get(WeakCache.java:127)
	at java.lang.reflect.Proxy.getProxyClass0(Proxy.java:419)
	at java.lang.reflect.Proxy.newProxyInstance(Proxy.java:719)
	at com.lw.designpattern.proxy.dynamic.DynamicProxyTest.main(DynamicProxyTest.java:12)

咦,報錯了。。。。提示Actor類不是一個接口,原因是啥,暫且按下不表,先改成接口再說

package com.lw.designpattern.proxy.dynamic;

public interface Actor {
    public void act();
}

增加實現類:

package com.lw.designpattern.proxy.dynamic;

public class BBFan implements Actor {

    @Override
    public void act() {
        System.out.println("某毯星戛納走紅毯");
    }
}

測試結果:

經紀人準備
某毯星戛納走紅毯
經紀人善後

OK,搞定,下面講講實現原理

 

JDK動態代理原理

在說原理之前,先搞清楚概念,所謂的動態,其實就是動態生成一個代理類Class,相比較靜態代理模式的手寫代理類,動態代理在這方面可謂是更加靈活。下面先看看生成的代理類是個什麼樣子。

代理類我是直接通過ProxyGenerator類生成的,這也是JDK源碼使用的類,所以生成的代理類中沒有包含自定義的代理功能。

當然也可以在測試方法中加上這句:System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

byte[] proxyClassFile = ProxyGenerator.generateProxyClass("ActorProxy", new Class[]{Actor.class});
try {
    FileOutputStream f = new FileOutputStream("ActorProxy.class");
    f.write(proxyClassFile);
    f.flush();
    f.close();
} catch (FileNotFoundException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
}

生成代理類class的反編譯源碼: 

import com.lw.pattern.proxy.dynamic.Actor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

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

    public ActorProxy(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    // 業務相關的核心方法
    public final void act() throws  {
        try {
            // 這邊通過父類的h屬性調用實際業務方法
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.lw.pattern.proxy.dynamic.Actor").getMethod("act");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

從這個源碼我們來解釋一下上面遺留的兩個問題:

一,代理類繼承了JDK的Proxy Class,因爲Java規範設計一個類只能繼承一個父類,所以,這也就解釋了爲什麼JDK的動態代理要求被代理類一定要實現接口。

二,Invocation的設計是爲了動態代理的調用,我們可以在Proxy類中找到關於Invocation的屬性定義:

    /**
     * the invocation handler for this proxy instance.
     * @serial
     */
    protected InvocationHandler h;

有了這個實例變量的定義,我們就可以把這個Invocation看作是動態代理的模板方法,這樣的話,每new一個新的被代理類,就不需要像靜態動態代理那樣修改代理類。

 

看到這裏,就會發現,動態代理真的沒有什麼高深的東西,本質上同靜態代理的實現思路也一致。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章