java jdk代理,手把手交你寫java代理

語言: CN / TW / HK

一:常用的java代理模式

        一般經常做java開發的知道java的代理模式一共有三種,第一種也就是靜態代理,這種用法比較簡單,沒有什麼魔法棒,比較好理解,另外兩種分別是JDK代理和cglib代理,他們分別是對接口代理和對class類本身進行代理,jdk代理要求類必須實現有一個或者多個接口,對接口進行字節碼增強在內存中實現新的class類去反射調用用户target的實現類,這裏需要説明的是不管是cglic代理也好還是jdk代理他們在內存中都要佔據方法區資源(jdk8 叫原空間),從而達到代理目的,而cglib代理是對class類本身進行字節碼增強配合fastclass來實現代理,關於更多的cglib和jdk代理相關的內容大家可以google搜索一下,網上有很多這裏不做再多的説明。下面我們摒棄jdk,和cglib的複雜源碼來自己實現一個代理模式,來更深刻的瞭解一下代理究竟是怎麼形成的。

二:java原生jdk代理demo和源碼分析

       代理模式是指給某一個對象提供一個代理對象,並由代理對象控制對原對象的引用。通俗的來講代理模式就是我們生活中常見的中介。這種模式有什麼用呢?它可以在原對象的基礎上增強原對象的功能,比如在原對象調用一個方法的前後進行日誌、事務操作等。Spring AOP就使用了代理模式。如何實現代理模式呢?首先來看靜態代理。靜態代理是指在程序運行前就已經存在的編譯好的代理類是為靜態代理。實現靜態代理有四個步驟:
     ①定義業務接口;
     ②被代理類實現業務接口;
     ③定義代理類並實現業務接口;
     ④最後便可通過客户端進行調用。(這裏可以理解成程序的main方法裏的內容)
     我們按照這個步驟去實現靜態代理。需求:在向數據庫添加一個用户時前後打印日誌。
 
JDK DEMO示例
 
IUserService.java
public interface IUserService {
    void add(String name);  
}

 

UserServiceImpl.java 

public class UserServiceImpl implements IUserService{

    @Override
    public void add(String name) {
        System.out.println("數據庫中插入:  "+name+" 的用户");
    }

}

MyInvocationHandler.java

public class MyInvocationHandler implements InvocationHandler {
    //被代理對象,Object類型
    private Object target;
    
    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("準備向數據庫中插入數據");
        Object returnvalue = method.invoke(target, args);
        System.out.println("插入數據庫成功");

        return returnvalue;
    }
}

  

測試類

public static void main(String[] args) {

        IUserService target = new UserServiceImpl();
        MyInvocationHandler handler = new MyInvocationHandler(target);
        IUserService proxyObject = (IUserService) Proxy.newProxyInstance(DynamicProxyTest.class.getClassLoader(),
                target.getClass().getInterfaces(), handler);
        proxyObject.add("張玉龍");
    }

  使用上非常簡單、網上demo也很多,不做充分講解,對jdk代理用法的小夥伴如果還不熟悉這塊代碼,就先了解一下jdk代理的使用方式,然後在回來繼續看下面的源碼分析


JDK代理源碼深度分析
 
這部分如果想要更快更好的理解,建議一邊對着源碼(本文JDK 1.8),一邊看着博客。畢竟自己親身實踐效果才好嘛。 Proxy.newProxyInstance( ClassLoaderloader, Class[] interfaces, InvocationHandler h) 產生了代理對象,所以我們進到 newProxyInstance 的實現:
 
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);
    }
}

  

 
 
這段代碼核心就是通過 getProxyClass0(loader, intfs)得到代理類的Class對象,然後通過Class對象得到構造方法,進而創建代理對象。下一步看 getProxyClass0這個方法。
 
//此方法也是Proxy類下的方法
    private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        //意思是:如果代理類被指定的類加載器loader定義了,並實現了給定的接口interfaces,
        //那麼就返回緩存的代理類對象,否則使用ProxyClassFactory創建代理類。
        return proxyClassCache.get(loader, interfaces);
    }

 這裏看到proxyClassCache,有Cache便知道是緩存的意思,正好呼應了前面Look up or generate the designated proxy class。查詢(在緩存中已經有)或生成指定的代理類的class對象這段註釋。

proxyClassCache是個WeakCache類的對象,調用proxyClassCache.get(loader, interfaces); 可以得到緩存的代理類或創建代理類(沒有緩存的情況)。説明WeakCache中有 get這個方法。先看下WeakCache類的定義(這裏先只給出變量的定義和構造函數):
 
//K代表key的類型,P代表參數的類型,V代表value的類型。
// WeakCache<ClassLoader, Class<?>[], Class<?>>  proxyClassCache  説明proxyClassCache存的值是Class<?>對象,正是我們需要的代理類對象。
final class WeakCache<K, P, V> {

    private final ReferenceQueue<K> refQueue
        = new ReferenceQueue<>();
    // the key type is Object for supporting null key
    private final ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>> map
        = new ConcurrentHashMap<>();
    private final ConcurrentMap<Supplier<V>, Boolean> reverseMap
        = new ConcurrentHashMap<>();
    private final BiFunction<K, P, ?> subKeyFactory;
    private final BiFunction<K, P, V> valueFactory;

  
    public WeakCache(BiFunction<K, P, ?> subKeyFactory,
                     BiFunction<K, P, V> valueFactory) {
        this.subKeyFactory = Objects.requireNonNull(subKeyFactory);
        this.valueFactory = Objects.requireNonNull(valueFactory);
    }

  

 其中map變量是實現緩存的核心變量,他是一個雙重的Map結構:   (key, sub-key) -> value。其中key是傳進來的Classloader進行包裝後的對象,sub-key是由WeakCache構造函數傳人的 KeyFactory()生成的。value就是產生代理類的對象,是由WeakCache構造函數傳人的 ProxyClassFactory()生成的

 好,大體上説完WeakCache這個類的作用,我們回到剛才 proxyClassCache.get(loader, interfaces);這句代碼。get是WeakCache裏的方法。源碼如下
//K和P就是WeakCache定義中的泛型,key是類加載器,parameter是接口類數組
public V get(K key, P parameter) {
        //檢查parameter不為空
        Objects.requireNonNull(parameter);
         //清除無效的緩存
        expungeStaleEntries();
        // cacheKey就是(key, sub-key) -> value裏的一級key,
        Object cacheKey = CacheKey.valueOf(key, refQueue);

        // lazily install the 2nd level valuesMap for the particular cacheKey
        //根據一級key得到 ConcurrentMap<Object, Supplier<V>>對象。如果之前不存在,則新建一個ConcurrentMap<Object, Supplier<V>>和cacheKey(一級key)一起放到map中。
        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;
            }
        }

        // create subKey and retrieve the possible Supplier<V> stored by that
        // subKey from valuesMap
        //這部分就是調用生成sub-key的代碼,上面我們已經看過怎麼生成的了
        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
        //通過sub-key得到supplier
        Supplier<V> supplier = valuesMap.get(subKey);
        //supplier實際上就是這個factory
        Factory factory = null;

        while (true) {
            //如果緩存裏有supplier ,那就直接通過get方法,得到代理類對象,返回,就結束了,一會兒分析get方法。
            if (supplier != null) {
                // supplier might be a Factory or a CacheValue<V> instance
                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
            //下面的所有代碼目的就是:如果緩存中沒有supplier,則創建一個Factory對象,把factory對象在多線程的環境下安全的賦給supplier。
            //因為是在while(true)中,賦值成功後又回到上面去調get方法,返回才結束。
            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);
                }
            }
        }
    }

  所以接下來我們看Factory類中的get方法。

public synchronized V get() { // serialize access
            // re-check
            Supplier<V> supplier = valuesMap.get(subKey);
            //重新檢查得到的supplier是不是當前對象
            if (supplier != this) {
                // something changed while we were waiting:
                // might be that we were replaced by a CacheValue
                // or were removed because of failure ->
                // return null to signal WeakCache.get() to retry
                // the loop
                return null;
            }
            // else still us (supplier == this)

            // create new value
            V value = null;
            try {
                 //代理類就是在這個位置調用valueFactory生成的
                 //valueFactory就是我們傳入的 new ProxyClassFactory()
                //一會我們分析ProxyClassFactory()的apply方法
                value = Objects.requireNonNull(valueFactory.apply(key, parameter));
            } finally {
                if (value == null) { // remove us on failure
                    valuesMap.remove(subKey, this);
                }
            }
            // the only path to reach here is with non-null value
            assert value != null;

            // wrap value with CacheValue (WeakReference)
            //把value包裝成弱引用
            CacheValue<V> cacheValue = new CacheValue<>(value);

            // put into reverseMap
            // reverseMap是用來實現緩存的有效性
            reverseMap.put(cacheValue, Boolean.TRUE);

            // try replacing us with CacheValue (this should always succeed)
            if (!valuesMap.replace(subKey, this, cacheValue)) {
                throw new AssertionError("Should not reach here");
            }

            // successfully replaced us with new CacheValue -> return the value
            // wrapped by it
            return value;
        }
    }

  撥雲見日,來到ProxyClassFactory的apply方法,代理類就是在這裏生成的。

//這裏的BiFunction<T, U, R>是個函數式接口,可以理解為用T,U兩種類型做參數,得到R類型的返回值
private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
        // prefix for all proxy class names
        //所有代理類名字的前綴
        private static final String proxyClassNamePrefix = "$Proxy";
        
        // next number to use for generation of unique proxy class names
        //用於生成代理類名字的計數器
        private static final AtomicLong nextUniqueNumber = new AtomicLong();

        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
              
            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            //驗證代理接口,可不看
            for (Class<?> intf : interfaces) {
                /*
                 * Verify that the class loader resolves the name of this
                 * interface to the same Class object.
                 */
                Class<?> interfaceClass = null;
                try {
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }
                if (interfaceClass != intf) {
                    throw new IllegalArgumentException(
                        intf + " is not visible from class loader");
                }
                /*
                 * Verify that the Class object actually represents an
                 * interface.
                 */
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
                /*
                 * Verify that this interface is not a duplicate.
                 */
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
            }
            //生成的代理類的包名 
            String proxyPkg = null;     // package to define proxy class in
            //代理類訪問控制符: public ,final
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

            /*
             * Record the package of a non-public proxy interface so that the
             * proxy class will be defined in the same package.  Verify that
             * all non-public proxy interfaces are in the same package.
             */
            //驗證所有非公共的接口在同一個包內;公共的就無需處理
            //生成包名和類名的邏輯,包名默認是com.sun.proxy,類名默認是$Proxy 加上一個自增的整數值
            //如果被代理類是 non-public proxy interface ,則用和被代理類接口一樣的包名
            for (Class<?> intf : interfaces) {
                int flags = intf.getModifiers();
                if (!Modifier.isPublic(flags)) {
                    accessFlags = Modifier.FINAL;
                    String name = intf.getName();
                    int n = name.lastIndexOf('.');
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                            "non-public interfaces from different packages");
                    }
                }
            }

            if (proxyPkg == null) {
                // if no non-public proxy interfaces, use com.sun.proxy package
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }

            /*
             * Choose a name for the proxy class to generate.
             */
            long num = nextUniqueNumber.getAndIncrement();
            //代理類的完全限定名,如com.sun.proxy.$Proxy0.calss
            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            /*
             * Generate the specified proxy class.
             */
            //核心部分,生成代理類的字節碼
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                //把代理類加載到JVM中,至此動態代理過程基本結束了
                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());
            }
        }
    }

  到這裏其實已經分析完了,但是本着深究的態度,決定看看JDK生成的動態代理字節碼是什麼,於是我們將字節碼保存到磁盤上的class文件中。代碼如下:

public static void main(String[] args) {

        IUserService target = new UserServiceImpl();
        MyInvocationHandler handler = new MyInvocationHandler(target);
        //第一個參數是指定代理類的類加載器(我們傳入當前測試類的類加載器)
        //第二個參數是代理類需要實現的接口(我們傳入被代理類實現的接口,這樣生成的代理類和被代理類就實現了相同的接口)
        //第三個參數是invocation handler,用來處理方法的調用。這裏傳入我們自己實現的handler
        IUserService proxyObject = (IUserService) Proxy.newProxyInstance(DynamicProxyTest.class.getClassLoader(),
                target.getClass().getInterfaces(), handler);
        proxyObject.add("張玉龍");
        
        String path = "D:/$Proxy0.class";
        byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", HelloworldImpl.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();
            }
        }
        
    }

   運行這段代碼,會在D盤生成一個名為$Proxy0.class的文件。通過反編譯工具,得到JDK為我們生成的代理類是這樣的:

// Decompiled by Jad v1.5.8e2. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://kpdus.tripod.com/jad.html
// Decompiler options: packimports(3) fieldsfirst ansi space 

import com.zhb.jdk.proxy.IUserService;
import java.lang.reflect.*;

public final class $Proxy0 extends Proxy
    implements IUserService
{

    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;
    //代理類的構造函數,其參數正是是InvocationHandler實例,
    //Proxy.newInstance方法就是通過通過這個構造函數來創建代理實例的
    public $Proxy0(InvocationHandler invocationhandler)
    {
        super(invocationhandler);
    }
     // Object類中的三個方法,equals,toString, hashCode
    public final boolean equals(Object obj)
    {
        try
        {
            return ((Boolean)super.h.invoke(this, m1, new Object[] {
                obj
            })).booleanValue();
        }
        catch (Error ) { }
        catch (Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final String toString()
    {
        try
        {
            return (String)super.h.invoke(this, m2, null);
        }
        catch (Error ) { }
        catch (Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }
    //接口代理方法
    public final void add(String s)
    {
        try
        {
            // invocation handler的 invoke方法在這裏被調用
            super.h.invoke(this, m3, new Object[] {
                s
            });
            return;
        }
        catch (Error ) { }
        catch (Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final int hashCode()
    {
        try
        {
            // 在這裏調用了invoke方法。
            return ((Integer)super.h.invoke(this, m0, null)).intValue();
        }
        catch (Error ) { }
        catch (Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }
    
    // 靜態代碼塊對變量進行一些初始化工作
    static 
    {
        try
        {
            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("com.zhb.jdk.proxy.IUserService").getMethod("add", new Class[] {
                Class.forName("java.lang.String")
            });
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
        }
        catch (NoSuchMethodException nosuchmethodexception)
        {
            throw new NoSuchMethodError(nosuchmethodexception.getMessage());
        }
        catch (ClassNotFoundException classnotfoundexception)
        {
            throw new NoClassDefFoundError(classnotfoundexception.getMessage());
        }
    }
}

生成了Object類的三個方法:toString,hashCode,equals。還有我們需要被代理的方法。

 

JDK代理類的cache clear機制

大家都知道、在項目中被代理的class越來越多,所以jdk會搞一個cache的方式來防止相同的代理接口重複生成class,影響性能不説,實現也不是很優雅,那麼現在就會有一個問題了,當classloader已經在內存中沒有依賴的時候,被代理的proxy class其實也沒有什麼意義了,這樣就需要清空無用的cache,java Proxy採用了非常巧妙的“弱引用機制”,我們來看下面的代碼

我們還是繼續看get方法的源碼

 

public V get(K key, P parameter) {
        Objects.requireNonNull(parameter);

        expungeStaleEntries();

        Object cacheKey = CacheKey.valueOf(key, refQueue);

        // lazily install the 2nd level valuesMap for the particular cacheKey
        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;
            }
        }
.......
}

其中源碼中有一個方法expungeStaleEntries、我們進去這個方法一窺究竟

private void expungeStaleEntries() {
        CacheKey<K> cacheKey;
        while ((cacheKey = (CacheKey<K>)refQueue.poll()) != null) {
            cacheKey.expungeFrom(map, reverseMap);
        }
    }

 在看看expungeFrom方法源碼幹了些什麼

void expungeFrom(ConcurrentMap<?, ? extends ConcurrentMap<?, ?>> map,
                         ConcurrentMap<?, Boolean> reverseMap) {
            // removing just by key is always safe here because after a CacheKey
            // is cleared and enqueue-ed it is only equal to itself
            // (see equals method)...
            ConcurrentMap<?, ?> valuesMap = map.remove(this);
            // remove also from reverseMap if needed
            if (valuesMap != null) {
                for (Object cacheValue : valuesMap.values()) {
                    reverseMap.remove(cacheValue);
                }
            }
        }

  代碼很清晰了,清空被代理的對象。現在的關鍵就是refQueue對象是怎麼來的。我們繼續找一下跟refQueue相關的源碼、在get中還有一段代碼是這樣的

Object cacheKey = CacheKey.valueOf(key, refQueue);

 

private static final class CacheKey<K> extends WeakReference<K> {

        // a replacement for null keys
        private static final Object NULL_KEY = new Object();

        static <K> Object valueOf(K key, ReferenceQueue<K> refQueue) {
            return key == null
                   // null key means we can't weakly reference it,
                   // so we use a NULL_KEY singleton as cache key
                   ? NULL_KEY
                   // non-null key requires wrapping with a WeakReference
                   : new CacheKey<>(key, refQueue);
        }

        private final int hash;

        private CacheKey(K key, ReferenceQueue<K> refQueue) {
            super(key, refQueue);
            this.hash = System.identityHashCode(key);  // compare by identity
        }
.....
}

 這樣看就非常清晰了、原來是CacheKey繼承了WeakReference弱引用機制,當弱引用依賴的key沒有引用的時候,當前失效的對象就會進入ReferenceQueue中來實現清空cache的功能、這種實現思路和ThreadLocal的實現原理是一樣的、大家有興趣可以去閲讀以下相關源碼。

 

三:手把手寫基於接口的java代理

上面我們分析了jdk動態代理源碼、那我們是不是可以自己用自己的方式去寫一個屬於自己的jdk代理呢,答案是可以的

首先我們寫一個基類,當然我並沒有在基類裏面寫什麼東西,只是模擬java中的proxy類而已,當然我們也可以豐富的去拓展一下這個類的方法,來實現更多的功能,讀者可以通過讀完這篇文章之後自己去考慮一下如何來拓展。

1 package meituan.zylproxy.handlder;
2 public class ZylProxy {
3     public ZylProxy(){
4     }
5 }

 

代理的核心接口,我們去做代理的時候一定是通過反射去調用的,不管jdk也好還是cglib也好,永遠也無法脱離反射,我們照貓畫虎,自己寫一個代理接口核心類,這並不是什麼難題,看起來和jdk的核心類接口也沒有什麼區別。

1 package meituan.zylproxy.handlder;
2 
3 import java.lang.reflect.Method;
4 
5 public interface ZYLInvocationHandler {
6 
7     public Object invoke(Object proxy, Method method, Object[] args)
8         throws Exception;
9 }

説明一下 第一個參數proxy是代表代理類,而不是用户自己寫的原生類實現。參數Method是接口的方法,args是運行時參數列表,在運行時傳遞過來的實際上就是實現類的參數,好了,下面讓我們去深入核心。

我們自定義兩個接口和接口的實現Idto,Idto2,和Dtoimpl如下:

1 package meituan.zylproxy.test.i;
2 
3 public interface Idto {
4 
5     public void add();
6     
7     public String get();
8     
9 }
package meituan.zylproxy.test.i;

public interface Idto2 {

    public void adda();
    
    public String geta();
    
}
package meituan.zylproxy.test.i.impl;

import meituan.zylproxy.test.i.Idto;
import meituan.zylproxy.test.i.Idto2;

public class DtoImpl implements Idto,Idto2{

    @Override
    public void add() {
        System.out.println("add");
        
    }

    @Override
    public String get() {
        System.out.println("get");
        return "return get";
    }

    @Override
    public void adda() {
        System.out.println("adda");
    }

    @Override
    public String geta() {
        System.out.println("geta");
        return "return geta";
    }

}

這是幾個再簡單不過的接口和實現類了,也沒有什麼可説的。接下來我們想對接口進行代理,無非是我們動態將接口進行實現,從而達到對使用者進行自定義handle接口暴露而已,下面看一下我們需要生成一個什麼樣的代理類。

import java.lang.reflect.Method;

import meituan.zylproxy.handlder.ZylProxy;
import meituan.zylproxy.handlder.ZYLInvocationHandler;
import meituan.zylproxy.test.i.Idto;
import meituan.zylproxy.test.i.Idto2;

public class IdtoPorxy extends ZylProxy implements Idto, Idto2 {
    public ZYLInvocationHandler zYLInvocationHandler;
    public static Method add1;
    public static Method get2;
    public static Method adda3;
    public static Method geta4;

    static {
        try {
            add1 = Class.forName ( "meituan.zylproxy.test.i.Idto" ).getMethod ( "add", new Class[0] );
            get2 = Class.forName ( "meituan.zylproxy.test.i.Idto" ).getMethod ( "get", new Class[0] );
            adda3 = Class.forName ( "meituan.zylproxy.test.i.Idto2" ).getMethod ( "adda", new Class[0] );
            geta4 = Class.forName ( "meituan.zylproxy.test.i.Idto2" ).getMethod ( "geta", new Class[0] );
        } catch (Exception e) {
        }
    }

    public IdtoPorxy(ZYLInvocationHandler zYLInvocationHandler) {
        this.zYLInvocationHandler = zYLInvocationHandler;
    }

    public void add() {
        Object[] o = {};
        try {
            this.zYLInvocationHandler.invoke ( this, add1, o );
            return;
        } catch (Throwable e) {
            e.printStackTrace ();
        }
    }

    public java.lang.String get() {
        Object[] o = {};
        try {
            return (java.lang.String) this.zYLInvocationHandler.invoke ( this, get2, o );
        } catch (Exception e) {
            e.printStackTrace ();
        }
        return null;
    }

    public void adda() {
        Object[] o = {};
        try {
            this.zYLInvocationHandler.invoke ( this, adda3, o );
            return;
        } catch (Throwable e) {
            e.printStackTrace ();
        }
    }

    public java.lang.String geta() {
        Object[] o = {};
        try {
            return (java.lang.String) this.zYLInvocationHandler.invoke ( this, geta4, o );
        } catch (Exception e) {
            e.printStackTrace ();
        }
        return null;
    }
}

這個類不是由用户寫的,而是我們動態生成的,對於jdk來説是生成了字節碼,對cglib來説是通過字節碼增強,其實實現的方式有多種,後面為了更方便大家理解我用字符串的形式來動態生成這麼一個"傢伙",先看看這個類幹了些什麼吧,也很簡單。

public class IdtoPorxy extends ZylProxy implements Idto, Idto2

首先是繼承了剛才我們所説的ZylProxy,留着今後拓展,可以參照java的Proxy,然後並且動態的實現了這兩個接口。很簡單

public ZYLInvocationHandler zYLInvocationHandler;
public IdtoPorxy(ZYLInvocationHandler zYLInvocationHandler) {
    this.zYLInvocationHandler = zYLInvocationHandler;
}

這個是通過構造函數傳進來一個handler對象,對實現類的操作都靠它了。

public static Method add1;
    public static Method get2;
    public static Method adda3;
    public static Method geta4;

    static {
        try {
            add1 = Class.forName ( "meituan.zylproxy.test.i.Idto" ).getMethod ( "add", new Class[0] );
            get2 = Class.forName ( "meituan.zylproxy.test.i.Idto" ).getMethod ( "get", new Class[0] );
            adda3 = Class.forName ( "meituan.zylproxy.test.i.Idto2" ).getMethod ( "adda", new Class[0] );
            geta4 = Class.forName ( "meituan.zylproxy.test.i.Idto2" ).getMethod ( "geta", new Class[0] );
        } catch (Exception e) {
        }
    }

枚舉出來所有的接口的方法,通過class.forname來獲取到Method元數據。備用

 

public void add() {
        Object[] o = {};
        try {
            this.zYLInvocationHandler.invoke ( this, add1, o );
            return;
        } catch (Throwable e) {
            e.printStackTrace ();
        }
    }

    public java.lang.String get() {
        Object[] o = {};
        try {
            return (java.lang.String) this.zYLInvocationHandler.invoke ( this, get2, o );
        } catch (Exception e) {
            e.printStackTrace ();
        }
        return null;
    }

    public void adda() {
        Object[] o = {};
        try {
            this.zYLInvocationHandler.invoke ( this, adda3, o );
            return;
        } catch (Throwable e) {
            e.printStackTrace ();
        }
    }

    public java.lang.String geta() {
        Object[] o = {};
        try {
            return (java.lang.String) this.zYLInvocationHandler.invoke ( this, geta4, o );
        } catch (Exception e) {
            e.printStackTrace ();
        }
        return null;
    }

上面是要枚舉出來所有的方法的實現,很簡單都一個模樣,把實現交給handler去做就可以了。至於怎麼實現靠handler,我們動態生成的這個類只負責委託,不做任何事情。看到這裏大家一定急不可待的想知道這個類怎麼生成的了,我把我寫的源碼給大家貼出來看一下。

package meituan.zylproxy.util;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

import meituan.zylproxy.test.i.Idto;
import meituan.zylproxy.test.i.Idto2;

public class ClassUtil {

    public static String mackProxyClass(Class<?> c) throws Exception{
        if(!c.isInterface()){
            throw new Exception("代理的類必須是接口");
        }

        StringBuffer importsp = new StringBuffer();
        importsp.append("import java.lang.reflect.Method;\n");
        importsp.append("import meituan.zylproxy.handlder.ZylProxy;\n");
        importsp.append("import meituan.zylproxy.handlder.ZYLInvocationHandler;\n");

        importsp.append("import " +c.getName() + ";\n");

        StringBuilder publicStaticMethods = new StringBuilder();
        
        //public static Method add;
        StringBuilder publicMethods = new StringBuilder();
        publicMethods.append("public ZYLInvocationHandler zYLInvocationHandler;\n");

        StringBuilder constructorsp = new StringBuilder();
        String interFaceName = c.getName().substring(c.getName().lastIndexOf(".")+1);
        constructorsp.append("public ").append("" + interFaceName + "Porxy").
                   append("(ZYLInvocationHandler zYLInvocationHandler) { "
                           + "this.zYLInvocationHandler = zYLInvocationHandler;"
                           + "}");

        publicStaticMethods.append(" static { try {  ");

        StringBuilder classsp = new StringBuilder();
        classsp.append("public class").append(" " + interFaceName + "Porxy").append(" extends ZylProxy implements ").append(interFaceName).append("{");
        
     
        StringBuilder allMethods = new StringBuilder();
        Method[] Methods = c.getMethods();
        
        int curr=0;
        for (Method m_:Methods) {
            curr++;
            publicMethods.append("public static Method ").append(m_.getName() + String.valueOf(curr)).append(";\n");
            
            publicStaticMethods.append("").append(m_.getName() +  String.valueOf(curr)).append("=");

            publicStaticMethods.append("Class.forName(\"" + c.getName() + "\")" + ".getMethod(\""+ m_.getName() +"\", ");
            
            StringBuilder sp =new StringBuilder();
            StringBuilder spArgs = new StringBuilder();
            spArgs.append("Object[] o ={");
            //public
             sp.append(Modifier.toString(m_.getModifiers()).replace("abstract", "")).append(" ");
            //void | java.lang.String
            sp.append(m_.getReturnType().getName()).append(" ");
            //add()|get()
            sp.append(m_.getName().concat("("));

            StringBuilder methodCLass = new StringBuilder();
             if(m_.getParameterTypes().length>0){
                Class<?>[] claszz = m_.getParameterTypes();
                int methodOffset = 0;
                methodCLass.append("new Class[] { ");
                for (Class<?> c_ : claszz) {
                    String paramStr = "obj" + String.valueOf(++methodOffset);
                    spArgs.append(paramStr.concat(","));
                    sp.append(c_.getName().toString().concat(" ").concat(paramStr)).append(",");
                    methodCLass.append("Class.forName(\"" + c_.getName()).append("\"),");
                }
                sp = new StringBuilder(sp.substring(0, sp.length()-1));
                 spArgs = new StringBuilder(spArgs.substring(0, spArgs.length()-1));
                 methodCLass = new StringBuilder(methodCLass.substring(0, methodCLass.length()-1));
            }

             if(methodCLass.length()>0){
                 methodCLass.append("}");
             } else{
                 methodCLass.append("new Class[0]");
             }
             sp.append("){\n");
            spArgs.append("}");
            sp.append(spArgs+";\n");
            
            if(sp.toString().contains("void")){
                sp.append("try {\n this.zYLInvocationHandler.invoke(this,").append(m_.getName() + String.valueOf(curr)).append(",").append("o);\n return;\n");
                sp.append("} catch (Throwable e) {e.printStackTrace();}}");

            } else{
                sp.append("try {return "
                        + "("
                        + m_.getReturnType().getName()
                        + ")"
                        + "this.zYLInvocationHandler.invoke(this,").append(m_.getName() + String.valueOf(curr)).append(",").append("o);\n");
            
                sp.append("} catch (Exception e) {e.printStackTrace();} return null;");

            }

            publicStaticMethods.append(methodCLass).append(");\n");
             allMethods.append(sp);
        }
        publicStaticMethods.append("} catch(Exception e){}}");
        classsp.append(publicMethods)
               .append(publicStaticMethods)
               .append(constructorsp).append(allMethods).append("}");
        classsp.append("}");
        importsp.append(classsp);
        return importsp.toString();
     }

    
    public static String mackMultiProxyClass(Class<?>[] cs) throws Exception{

        StringBuffer importsp = new StringBuffer();
        importsp.append("import java.lang.reflect.Method;\n");
        importsp.append("import meituan.zylproxy.handlder.ZylProxy;\n");
        importsp.append("import meituan.zylproxy.handlder.ZYLInvocationHandler;\n");
        
        StringBuilder publicStaticMethods = new StringBuilder();
        publicStaticMethods.append(" static { try {  ");
        
        //public static Method add;
        StringBuilder publicMethods = new StringBuilder();
        publicMethods.append("public ZYLInvocationHandler zYLInvocationHandler;\n");
                
        int curr=0;
        
        StringBuilder constructorsp = new StringBuilder();
        String interFaceName = cs[0].getName().substring(cs[0].getName().lastIndexOf(".")+1);
        constructorsp.append("public ").append("" + interFaceName + "Porxy").
                   append("(ZYLInvocationHandler zYLInvocationHandler) { "
                           + "this.zYLInvocationHandler = zYLInvocationHandler;"
                           + "}");
        
        StringBuilder allMethods = new StringBuilder();
        
        StringBuilder classsp = new StringBuilder();
        classsp.append("public class").append(" " + interFaceName + "Porxy").append(" extends ZylProxy implements ");
        
        for (Class<?> c:cs) {
            if(!c.isInterface()){
                throw new Exception("代理的類必須是接口");
            }
            
            classsp.append(c.getName().substring(c.getName().lastIndexOf(".")+1)).append(",");
            
            importsp.append("import " +c.getName() + ";\n");
            
            
            Method[] Methods = c.getMethods();
            
            
            for (Method m_:Methods) {
                   curr++;
                publicMethods.append("public static Method ").append(m_.getName() + String.valueOf(curr)).append(";\n");
                
                publicStaticMethods.append("").append(m_.getName() +  String.valueOf(curr)).append("=");

                publicStaticMethods.append("Class.forName(\"" + c.getName() + "\")" + ".getMethod(\""+ m_.getName() +"\", ");
                
                StringBuilder sp =new StringBuilder();
                StringBuilder spArgs = new StringBuilder();
                spArgs.append("Object[] o ={");
                //public
                 sp.append(Modifier.toString(m_.getModifiers()).replace("abstract", "")).append(" ");
                //void | java.lang.String
                sp.append(m_.getReturnType().getName()).append(" ");
                //add()|get()
                sp.append(m_.getName().concat("("));

                StringBuilder methodCLass = new StringBuilder();
                 if(m_.getParameterTypes().length>0){
                    Class<?>[] claszz = m_.getParameterTypes();
                    int methodOffset = 0;
                    methodCLass.append("new Class[] { ");
                    for (Class<?> c_ : claszz) {
                        String paramStr = "obj" + String.valueOf(++methodOffset);
                        spArgs.append(paramStr.concat(","));
                        sp.append(c_.getName().toString().concat(" ").concat(paramStr)).append(",");
                        methodCLass.append("Class.forName(\"" + c_.getName()).append("\"),");
                    }
                    sp = new StringBuilder(sp.substring(0, sp.length()-1));
                     spArgs = new StringBuilder(spArgs.substring(0, spArgs.length()-1));
                     methodCLass = new StringBuilder(methodCLass.substring(0, methodCLass.length()-1));
                }

                 if(methodCLass.length()>0){
                     methodCLass.append("}");
                 } else{
                     methodCLass.append("new Class[0]");
                 }
                 sp.append("){\n");
                spArgs.append("}");
                sp.append(spArgs+";\n");
                
                if(sp.toString().contains("void")){
                    sp.append("try {\n this.zYLInvocationHandler.invoke(this,").append(m_.getName() + String.valueOf(curr)).append(",").append("o);\n return;\n");
                    sp.append("} catch (Throwable e) {e.printStackTrace();}}");

                } else{
                    sp.append("try {return "
                            + "("
                            + m_.getReturnType().getName()
                            + ")"
                            + "this.zYLInvocationHandler.invoke(this,").append(m_.getName() + String.valueOf(curr)).append(",").append("o);\n");
                
                    sp.append("} catch (Exception e) {e.printStackTrace();} return null;}");

                }

                publicStaticMethods.append(methodCLass).append(");\n");
                 allMethods.append(sp);
            }
            
        }
        
        classsp = new StringBuilder(classsp.substring(0, classsp.length()-1)).append("{");
         
        publicStaticMethods.append("} catch(Exception e){}}");
        classsp.append(publicMethods)
               .append(publicStaticMethods)
               .append(constructorsp).append(allMethods).append("");
        classsp.append("}");
        importsp.append(classsp);
        return importsp.toString();
     }
    
    
    public static void main(String[] args) throws Exception {
        System.out.println(mackMultiProxyClass(new Class<?>[]{Idto.class}));
    }
}

看起來很複雜,仔細看一下就看了那麼幾個事情,把一個接口class或者多個接口class變成純字符串的過程,一共兩個方法,一個是單接口的實現,很早之前寫的,第二個方法是多接口的實現支持多接口,只需要傳一個class對象就會生成代理類的字符串,這裏僅僅是字符串,需要編譯成class使用。那麼如何編譯成class呢。通過java中的工具類 JavaCompiler很簡單的就可以生成了。我們來看兩個工具類實現

package meituan.zylproxy;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringReader;
import java.net.URI;
import java.nio.CharBuffer;
import java.nio.file.WatchEvent.Kind;
import java.util.HashMap;
import java.util.Map;

import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;

@SuppressWarnings("unchecked")
final class MemoryJavaFileManager extends ForwardingJavaFileManager {

    private final static String EXT = ".java";

    private Map<String, byte[]> classBytes;

    public MemoryJavaFileManager(JavaFileManager fileManager) {
        super(fileManager);
        classBytes = new HashMap<String, byte[]>();
    }

    public Map<String, byte[]> getClassBytes() {
        return classBytes;
    }

    public void close() throws IOException {
        classBytes = new HashMap<String, byte[]>();
    }

    public void flush() throws IOException {
    }
 
    private static class StringInputBuffer extends SimpleJavaFileObject {
        final String code;

        StringInputBuffer(String name, String code) {
            super(toURI(name), Kind.SOURCE);
            this.code = code;
        }

        public CharBuffer getCharContent(boolean ignoreEncodingErrors) {
            return CharBuffer.wrap(code);
        }

        public Reader openReader() {
            return new StringReader(code);
        }
    }
 
    private class ClassOutputBuffer extends SimpleJavaFileObject {
        private String name;

        ClassOutputBuffer(String name) {
            super(toURI(name), Kind.CLASS);
            this.name = name;
        }

        public OutputStream openOutputStream() {
            return new FilterOutputStream(new ByteArrayOutputStream()) {
                public void close() throws IOException {
                    out.close();
                    ByteArrayOutputStream bos = (ByteArrayOutputStream) out;
                    classBytes.put(name, bos.toByteArray());
                }
            };
        }
    }

    public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location,
                                               String className,
                                               JavaFileObject.Kind kind,
                                               FileObject sibling) throws IOException {
        if (kind == JavaFileObject.Kind.CLASS) {
            return new ClassOutputBuffer(className);
        } else {
            return super.getJavaFileForOutput(location, className, kind, sibling);
        }
    }

    static JavaFileObject makeStringSource(String name, String code) {
        return new StringInputBuffer(name, code);
    }

    static URI toURI(String name) {
        File file = new File(name);
        if (file.exists()) {
            return file.toURI();
        } else {
            try {
                final StringBuilder newUri = new StringBuilder();
                newUri.append("mfm:///");
                newUri.append(name.replace('.', '/'));
                if (name.endsWith(EXT)) newUri.replace(newUri.length() - EXT.length(), newUri.length(), EXT);
                return URI.create(newUri.toString());
            } catch (Exception exp) {
                return URI.create("mfm:///com/sun/script/java/java_source");
            }
        }
    }
}

 

package meituan.zylproxy;

import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

public class DynamicLoader {

    public static Map<String, byte[]> compile(String javaSrc) {
        Pattern pattern = Pattern.compile("public\\s+class\\s+(\\w+)");

        Matcher matcher = pattern.matcher(javaSrc);

        if (matcher.find())
            return compile(matcher.group(1) + ".java", javaSrc);
        return null;
    }
    

    public static Map<String, byte[]> compile(String javaName, String javaSrc) {
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager stdManager = compiler.getStandardFileManager(null, null, null);

        try (MemoryJavaFileManager manager = new MemoryJavaFileManager(stdManager)) {
            JavaFileObject javaFileObject = manager.makeStringSource(javaName, javaSrc);
            JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, Arrays.asList(javaFileObject));
            if (task.call())
                return manager.getClassBytes();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static class MemoryClassLoader extends URLClassLoader {

        Map<String, byte[]> classBytes = new HashMap<String, byte[]>();

        public MemoryClassLoader(Map<String, byte[]> classBytes) {
            super(new URL[0], MemoryClassLoader.class.getClassLoader());
            this.classBytes.putAll(classBytes);
        }

        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            byte[] buf = classBytes.get(name);
            if (buf == null) {
                return super.findClass(name);
            }
            classBytes.remove(name);
            return defineClass(name, buf, 0, buf.length);
        }
    }
}

通過DynamicLoader的compile方法可以把純字符串的str轉成byte[]數組,有了byte[]數組就可以很方便的獲取到class對象了,自定義一個MemoryClassLoader通過defineClass方法來獲取到class對象。這樣基本所有的事情都做完了。下面我們寫一個工廠類來獲取代理類。

package meituan.zylproxy.util;

import java.util.Map;

import meituan.zylproxy.DynamicLoader;
import meituan.zylproxy.handlder.ZYLInvocationHandler;

public class PorxyFactory {

    
    //單interface的時候用
    public static Object newProxyInstance(Class<?> c,ZYLInvocationHandler h) throws Exception{

        String classStr = ClassUtil.mackProxyClass(c);
        Map<String, byte[]> m = DynamicLoader.compile(classStr);
        DynamicLoader.MemoryClassLoader classLoader = new DynamicLoader.MemoryClassLoader(m);
        Class<?> proxy =classLoader.loadClass(m.keySet().toArray(new String[0])[0]);
        return proxy.getConstructor(ZYLInvocationHandler.class).newInstance(h);
    }

    //多interface的時候用
    public static Object newProxyInstancewWithMultiClass(Class<?>[] c,ZYLInvocationHandler h) throws Exception{

        String classStr = ClassUtil.mackMultiProxyClass(c);
        System.out.println (classStr);
        Map<String, byte[]> m = DynamicLoader.compile(classStr);
        DynamicLoader.MemoryClassLoader classLoader = new DynamicLoader.MemoryClassLoader(m);
        Class<?> proxy =classLoader.loadClass(m.keySet().toArray(new String[0])[0]);
        return proxy.getConstructor(ZYLInvocationHandler.class).newInstance(h);
    }
}

 

最後一步我們測試一下結果吧,寫一個測試類

package meituan.zylproxy.test;

import meituan.zylproxy.handlder.Hander;
import meituan.zylproxy.test.i.Idto;
import meituan.zylproxy.test.i.impl.DtoImpl;
import meituan.zylproxy.util.PorxyFactory;

public class ZylPorxyTest {

     public static void main(String[] args) throws Exception {

         Idto d = (Idto) PorxyFactory.newProxyInstancewWithMultiClass(DtoImpl.class.getInterfaces(), new Hander(new DtoImpl()));
         d.add();
    }
}

很簡單,第一個參數是所有的接口,第二個是handler實現。最後我們看看結果。

 

大功告成。

作者簡介:就職美團外賣業務架構組,有更多的更多的源碼交流,請加羣825199617交流,spring源碼,spring mvc源碼,手寫企業級高可用rpc框架,java企業級熱部署解決方案等等更多精彩源碼,等你來。