1. 程式人生 > >spring中的設計模式——動態代理(一)

spring中的設計模式——動態代理(一)

前言

今天一定要把這一篇部落格補上,前段時間看了看動態代理的jdk方式如何實現,用起來簡單,但是jdk中的實現就不那麼容易了~~

代理模式的特點
  1. 有執行者、被代理者兩種角色
  2. 這件事一定要做,自己不想做,或者沒有時間做
  3. 執行者需要獲取被代理人的資料
    例子:取快遞、中介、媒人等
代理模式的應用小例子——Celine取快遞

celine的快遞到了,但是她去公司上班了,所以她爸幫她取得快遞。從這個例子中,我們抽象出了被代理人(celine),代理人(她爸爸),介面(取快遞)

(1) 介面

public interface Receive {
    public void Receive();
}

(2)celine

/**
 * 被代理類
 */
public class Celine implements Receive {

    @Override
    public void Receive() {
        System.out.println("Celine在上班,沒有辦法取快遞");
    }

}

(3)代理類

/**
 * 代理類需要實現InvocationHandler介面,並且重寫invoke方法,其中的method.invoke(target, args)就是celine具體實現介面的方法
 */
public class CelineFather implements InvocationHandler { private Object target; public CelineFather(Object target) { super(); this.target=target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { method.invoke(target, args); System.out.println("celine她爸取得快遞"
); return null; } }

(4)測試類

public class TestJdkDynamicProxy {
    public static void main(String[] args){
        Celine celine=new Celine();
        Class celineClass=celine.getClass();
        //通過newProxyInstance生成代理物件
        Receive Receiver = (Receive)Proxy.newProxyInstance(celineClass.getClassLoader(), celineClass.getInterfaces(),
                 new CelineFather(celine));
        //實際呼叫的是代理類中的invoke方法
        Receiver.Receive();
    }
}
JDK實現原理
  1. 拿到被代理物件的引用,然後獲取他的介面
  2. jdk代理重新生成一個類,同時實現代理物件所實現的介面(所以jdk的動態代理必須有介面)
  3. 拿到物件的引用
  4. 重新動態生成一個class位元組碼
  5. 重新編譯
jdk原始碼中幾個重要的方法

首先提出幾個疑問:
1.為什麼最後真正實現的是代理類的invoke方法?
2.為什麼newProxyInstance返回值要強轉成介面的例項?
3.newProxyInstance沒有轉成介面類之前是什麼?

我們從newProxyInstance這個方法入手,首先這個方法傳入了三個引數,分別是Celine的類載入器,Celine實現的介面陣列,CelineFather的物件

這個方法為我們做了三件比較重要的事情:
1、生成代理類
2、獲得代理類的建構函式
3、根據建構函式例項化代理類(生成代理類的例項並把MyInvocationHandler的例項傳給它的構造方法 )

 @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;
                    }
                });
            }
            //生成代理類的例項並把MyInvocationHandler的例項傳給它的構造方法 
            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);
        }
    }

getProxyClass0(loader, intfs)這個方法是最為重要的,他會生成一個$Proxy0的類,經過反編譯,我們看看這個類是什麼樣子的(下邊程式碼來源於網路,我沒有下載反編譯工具,所以就在網上找了一個)

這個類他會實現我們的被代理類中所有的方法,最為重要的是其中的super.h.invoke(this, m3, null); 方法,這也就是為什麼呼叫invoke方法實現的確實代理類的invoke方法,而且invoke方法會呼叫Celine的receive方法,第二個引數m3就是被代理類物件的receive的方法,這也解決了我們的三個疑問

import dynamic.proxy.UserService;  
import java.lang.reflect.*;  

public final class $Proxy11 extends Proxy  
    implements UserService  
{  

    // 構造方法,引數就是剛才傳過來的MyInvocationHandler類的例項  
    public $Proxy11(InvocationHandler invocationhandler)  
    {  
        super(invocationhandler);  
    }  

    public final boolean equals(Object obj)  
    {  
        try  
        {  
            return ((Boolean)super.h.invoke(this, m1, new Object[] {  
                obj  
            })).booleanValue();  
        }  
        catch(Error _ex) { }  
        catch(Throwable throwable)  
        {  
            throw new UndeclaredThrowableException(throwable);  
        }  
    }  

    /** 
     * 這個方法是關鍵部分 
     */  
    public final void add()  
    {  
        try  
        {  
            // 實際上就是呼叫MyInvocationHandler的public Object invoke(Object proxy, Method method, Object[] args)方法,第二個問題就解決了  
            super.h.invoke(this, m3, null);  
            return;  
        }  
        catch(Error _ex) { }  
        catch(Throwable throwable)  
        {  
            throw new UndeclaredThrowableException(throwable);  
        }  
    }  

    public final int hashCode()  
    {  
        try  
        {  
            return ((Integer)super.h.invoke(this, m0, null)).intValue();  
        }  
        catch(Error _ex) { }  
        catch(Throwable throwable)  
        {  
            throw new UndeclaredThrowableException(throwable);  
        }  
    }  

    public final String toString()  
    {  
        try  
        {  
            return (String)super.h.invoke(this, m2, null);  
        }  
        catch(Error _ex) { }  
        catch(Throwable throwable)  
        {  
            throw new UndeclaredThrowableException(throwable);  
        }  
    }  

    private static Method m1;  
    private static Method m3;  
    private static Method m0;  
    private static Method m2;  

    // 在靜態程式碼塊中獲取了4個方法:Object中的equals方法、UserService中的add方法、Object中的hashCode方法、Object中toString方法  
    static   
    {  
        try  
        {  
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {  
                Class.forName("java.lang.Object")  
            });  
            m3 = Class.forName("dynamic.proxy.UserService").getMethod("add", 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]);  
        }  
        catch(NoSuchMethodException nosuchmethodexception)  
        {  
            throw new NoSuchMethodError(nosuchmethodexception.getMessage());  
        }  
        catch(ClassNotFoundException classnotfoundexception)  
        {  
            throw new NoClassDefFoundError(classnotfoundexception.getMessage());  
        }  
    }  
}