設計模式之動態代理模式源碼分析

1、動態代理的實現

 

JDK動態代理java

jdk自帶的動態代理主要是經過實現InvocationHandler數組

InvocationHandler的主要方法緩存

Object invoke(Object proxy, Method method,Object[] args)throws Throwable安全

在代理實例上處理方法調用並返回結果。在與方法關聯的代理實例上調用方法時,將在調用處理程序上調用此方法。即調用真實業務的方法都會進入到此invoke方法,至於爲何,稍後再說明多線程

方法詳細介紹app

  • 參數:proxy - 調用方法的代理實例對象 。
  • Method-指代具體被代理的方法。 
    args -包含傳入代理實例上方法調用的參數,若是接口方法不使用參數,則爲 null。ide

  • return: 從代理實例的方法調用返回的值。函數

  • throws: Throwable - 從代理實例上的方法調用拋出的異常。測試

少說點廢話吧 ,關於什麼是動態代理網上不少介紹,這裏直接從源碼開始。。ui

1.業務接口

public interface HelloWorld {
    void say();
}

2.業務實現類

public class HelloWorldImp implements HelloWorld {
    @Override
    public void say() {
        System.out.println("hello world!");
    }
}

3.代理類

public class HelloProxy implements InvocationHandler {

    private Object target;//被代理對象

    public Object bind(Object target){
        this.target = target;
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("berfor.....");
        method.invoke(target, args);
        System.out.println("after.....");
        return null;
    }
}

4.測試類

public class Client {

    public static void main(String[] args) {
        HelloProxy h = new HelloProxy();
        HelloWorld helloWorld = (HelloWorld) h.bind(new HelloWorldImp());
        helloWorld.say();
    }
}

5.運行結果

2、原理

        咱們利用ProxyGenerator類的generateProxyClass方法生成代理對象的class文件在idea中打開,豁然開朗其實主要一步就在於生成類的過程,類文件中創建被代理對象接口的invoke調用

String path = "D://aaa.class";
byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", HelloWorldImp.class.getInterfaces());
FileOutputStream out = null;

try {
    out = new FileOutputStream(path);
    out.write(classFile);
    out.flush();
} catch (Exception e) {
    e.printStackTrace();
} finally {
    try {
        out.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

下面直接看源碼吧

public final class $Proxy0 extends Proxy implements HelloWorld {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
        } 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 say() throws  {
        try {
            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)).intValue();
        } 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.canyou.proxy.HelloWorld").getMethod("say");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

        生成的代理對象一個有4個方法,其中就有咱們的say方法,裏面調用handle的invoke方法,其餘三個是全部類都有的,直接用的Method的反射機制實現的代理。

3、代理類的生成

        那jdk源碼是怎麼生成代理類的呢?

首先看Proxy類的newProxyInstance方法吧

/**
 * 這裏有三個參數,
 * 第一個是傳入classloader,通常狀況是傳入當前的classloader.
 * 第二個參數表示的是接口,
 * 第三個是Invocationhandler
 */
@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);
    }

    //查找或者生成代理類
    Class<?> cl = getProxyClass0(loader, intfs);

    //生成構造函數
    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);
    }
}

而後看getProxyClass0方法

private static Class<?> getProxyClass0(ClassLoader loader,
                                       Class<?>... interfaces) {
    if (interfaces.length > 65535) {
        throw new IllegalArgumentException("interface limit exceeded");
    }

    //從緩存中獲取,若是不存在就建立
    return proxyClassCache.get(loader, interfaces);
}

使用proxyClassCache作緩存,其目的是爲了複用,同時防止多線程重複建立

//獲取或生成代理類 此處由於不是線程安全的作了屢次判斷
public V get(K key, P parameter) {
    Objects.requireNonNull(parameter);
    //刪除過時條目
    expungeStaleEntries();
    //建立cacheKey
    Object cacheKey = CacheKey.valueOf(key, refQueue);

    //查看key是否已經存在valuemaps中
    ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
    if (valuesMap == null) { //不存在的話經過,再次嘗試嘗試獲取,若是沒有就插入
        ConcurrentMap<Object, Supplier<V>> oldValuesMap
            = map.putIfAbsent(cacheKey,
                              valuesMap = new ConcurrentHashMap<>());
        if (oldValuesMap != null) {
            valuesMap = oldValuesMap;
        }
    }

    //生成代理對象的key 爲弱引用類型,這裏重要的subKeyFactory.apply方法
    Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
    //嘗試從valuemap中獲取
    Supplier<V> supplier = valuesMap.get(subKey);
    Factory factory = null;
    //這個後面的邏輯基本上是若是建立已經有了 就替換或者直接添加到緩存
    while (true) {
        if (supplier != null) {

            V value = supplier.get();
            if (value != null) {
                return value;
            }
        }
        // else no supplier in cache
        // or a supplier that returned null (could be a cleared CacheValue
        // or a Factory that wasn't successful in installing the CacheValue)

        // lazily construct a Factory
        if (factory == null) {
            factory = new Factory(key, parameter, subKey, valuesMap);
        }

        if (supplier == null) {
            supplier = valuesMap.putIfAbsent(subKey, factory);
            if (supplier == null) {
                // successfully installed Factory
                supplier = factory;
            }
            // else retry with winning supplier
        } else {
            if (valuesMap.replace(subKey, supplier, factory)) {
                // successfully replaced
                // cleared CacheEntry / unsuccessful Factory
                // with our Factory
                supplier = factory;
            } else {
                // retry with current supplier
                supplier = valuesMap.get(subKey);
            }
        }
    }
}

ProxyClassFactory.apply中的重要片斷方法,全部代理的class對象就這麼來的

/*
* 這裏纔是重點  就像以前生成的class文件同樣
* 生成代理類的字節數組
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
    proxyName, interfaces, accessFlags);
try {
    return defineClass0(loader, proxyName,
                        proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
    /*
     * A ClassFormatError here means that (barring bugs in the
     * proxy class generation code) there was some other
     * invalid aspect of the arguments supplied to the proxy
     * class creation (such as virtual machine limitations
     * exceeded).
     */
    throw new IllegalArgumentException(e.toString());
}
//生成代理類
public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
    ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
    /**
     * 生成具體文件字節數組
     * 1.找到全部接口的方法
     * 2.添加object類的三個方法 tostring hashcode equils
     * 3.遍歷生成具體的代理方法,代理方法的邏輯都想似,回調咱們的代理類
     */
    final byte[] var4 = var3.generateClassFile();
    // private static final boolean saveGeneratedFiles = GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"))).booleanValue();
    //設置sun.misc.ProxyGenerator.saveGeneratedFiles = true,就會生成代理類class的文件
    if (saveGeneratedFiles) {
        AccessController.doPrivileged(new PrivilegedAction<Void>() {
            public Void run() {
                try {
                    int var1 = var0.lastIndexOf(46);
                    Path var2;
                    if (var1 > 0) {
                        //生成path 將.替換成系統文件分隔符
                        Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
                        //建立文件夾
                        Files.createDirectories(var3);
                        //具體文件
                        var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
                    } else {
                        //沒包就放在項目根目錄下
                        var2 = Paths.get(var0 + ".class");
                    }
                    //寫入到文件中
                    Files.write(var2, var4, new OpenOption[0]);
                    return null;
                } catch (IOException var4x) {
                    throw new InternalError("I/O exception saving generated file: " + var4x);
                }
            }
        });
    }

    return var4;
}

4、總結

        之前老是知道有動態代理這麼一回事,理解也不是很深,經過源代碼的閱讀,對JDK的動態代理實現清晰不少吧,應該對後面的Spring AOP的具體實現會更有幫助。不過裏面也有不少地方沒有深刻了,仍是能力不夠吧,好比緩存代理,權限驗證等。

相關文章
相關標籤/搜索