Java高級--動態(tài)代理

什么是代理模式?

代理模式就是為其他對象提供一種代理來對該對象進行訪問控制。

生活實例:

        1)房產中介,他就是一個代理,由房東全權授權中介來進行房產的售前咨詢,相關事宜的處理等。

        2)滴滴代駕,就是授權代駕可以駕駛你的車輛,并將車輛行駛至目的地,期間代駕可以進行對車輛的一些簡單的操作。

代理---“鐵三角”:

  1. 抽象角色:

    為“代理角色”和“真實角色”提供的一套對外的公共方法(一般定義為接口)
interface ProxyInterface {
    fun confirmAction(inputString :String,name:String);
}
  1. 真實角色:

     真正的業(yè)務實現邏輯,他需要實現“抽象角色”中的接口;
    
    class RealInstance : ProxyInterface {
    
        private val TAG  = "RealInstance";
        override fun confirmAction(inputString: String,name: String) {
            Log.e(TAG,"真實實例對象----"+"調用confirmAction()")
        }
    }
    
  1. 代理角色:

     他同“真實角色”一樣,實現了“抽象角色”中的接口,同時擁有對“真實角色”的引用,然后通過“真實角色”的引用來實現“抽象角色”中的抽象方法,且可以自己在抽象方法中增加自己的一些操作等。
    
    class ProxyInstance(realInstance: RealInstance) : ProxyInterface {
        val TAG = "ProxyInstance";
    
        var mRealInstance: RealInstance = realInstance
        override fun confirmAction(inputString: String,name:String) {
            Log.e(TAG, "我是代理,我的confirmAction被調用")
            mRealInstance.confirmAction(inputString,name)
        }
    }
    

    三者之間的類關系如圖所示:


    鐵三角類圖.png

了解了上面的“鐵三角”,那相信我們對代理模式就已經完全明白了代理模式了。。。。很簡單吧?。。?!別高興的太早了,一步一步帶你進去深淵。。。。

代理的用途:

  1. 通過引入代理對象的方式來間接的訪問目標對象,防止直接訪問目標對象給系統(tǒng)帶來不必要的復雜性;
  2. 通過代理對象進行對目標對象的訪問控制;

好,那我們就來聊聊怎么使用? 今天先聊一下靜態(tài)代理和動態(tài)代理。

代理使用:

1)靜態(tài)代理:

        顧名思義,就是我們在實現代理模式時,在編輯階段就需要將對象的代理者都準備好,在程序運行時使用。
val proxyInstance = ProxyInstance(RealInstance())
proxyInstance.confirmAction("測試代理模式","")

缺點:

1.當需要代理的真實角色多時,我們的代理對象是不是也需要很多,這樣就會出現代理對象和代碼量變大的情況

2.如果我們想要擴展一下抽象接口的方法,那么真實角色和代理角色都需要維護,這樣是不是維護、擴展能力也比較差

既然靜態(tài)代理有那么明顯的缺點,我們有沒有辦法解決呢,答案是肯定的,這就是我們下面的動態(tài)代理。

2)動態(tài)代理:

        相對于靜態(tài)代理而言,動態(tài)代理則是在程序運行時再創(chuàng)建對象的代理類和實例,也就是說有程序幫我們去創(chuàng)建代理類及其實例,這樣效率很明顯要低些。

JDK已經為我們提供好了API了,我們直接用JDK API就可以了,如下:

 val newProxyInstance = Proxy.newProxyInstance(RealInstance::class.java.classLoader,
                arrayOf(ProxyInterface::class.java),
                object : InvocationHandler {
                    override fun invoke(proxy: Any?, method: Method?, args: Array<out Any>?): Any? {
                        if (args?.size!! > 1) {
                            return method?.invoke(RealInstance(), args[0],args[1])
                        }
                        return null
                    }
                }
            ) as ProxyInterface

newProxyInstance.confirmAction("hello","我是代理對象");   

上述代碼中,有兩個關鍵的API: Proxy和InvocationHandler,下面我們就深入“敵營”來了解一下。

源碼剖析:

Proxy 提供了創(chuàng)建動態(tài)代理類和實例的靜態(tài)方法,它也是有這些方法穿件的所有動態(tài)代理類的超類。是一個實現在運行時進行創(chuàng)建直接接口列表的類。

先來看看動態(tài)代理實現的流程圖,


動態(tài)代理原理流程圖.jpg

然后再看源碼,下面源碼已經做了詳細的備注。

Proxy 類中的成員變量和方法:

package java.lang.reflect;//在此包下

/////////////////////////////////////////////////////////////////////////////////

//靜態(tài)成員

  private static final Class<?>[] constructorParams =
        { InvocationHandler.class };


/**
* 返回指定接口的代理類的實例,該接口將方法調用分派給指定的調用處理程序。 
*----loader - 類加載器來定義代理類---當前真實角色的類加載器
*----interfaces - 代理類實現的接口列表   他是一個數組:因為一個代理可以代理多個實例對象
*----h - 調度方法調用的調用處理函數 
*/
  @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        //復制一份抽象接口類,并保存到數組中
        final Class<?>[] intfs = interfaces.clone();

        /*
         * Look up or generate the designated proxy class.
         * 查找或生成代理類
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            //獲取代理類的public構造方法的引用
            final Constructor<?> cons = cl.getConstructor(constructorParams);//constructorParams 靜態(tài)成員
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                cons.setAccessible(true);
            }
            
            //使用帶指定參數:h的構造器cons來生成該構造器所代表的代理類的實例,并將該代理類實例返回
            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);
        }
    }


///////////////////////////////////////////////////////////
  /**
     * a cache of proxy classes
     代理類的緩存
     */
    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());


 /**
     * Generate a proxy class.  Must call the checkProxyAccess method
     * to perform permission checks before calling this.
     
     從緩存中讀取代理類,如果存在則直接返回該代理類,否則創(chuàng)建該代理類
     */
    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
        return proxyClassCache.get(loader, interfaces);
    }

代理的緩存WeakCache

package java.lang.reflect;


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

        expungeStaleEntries();

        //使用外部傳入的key 生成cacheKey
        Object cacheKey = CacheKey.valueOf(key, refQueue);

        // lazily install the 2nd level valuesMap for the particular cacheKey
     //線程安全的原子操作擔保 ConcurrentMap
        ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
        if (valuesMap == null) {
            //從原緩存map中查找cacheKey ,如果找不到則創(chuàng)建一個新的map 添加到緩存map中,并將新的map一起返回
            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
         //1.使用可以生成subkey ,從新的map獲取可以對應的Supplier提供者
        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
        Supplier<V> supplier = valuesMap.get(subKey);
        Factory factory = null;

         //無限循環(huán)來查找值,如果找到就返回,沒有找到就創(chuàng)建一個新的值
        while (true) {
            
            if (supplier != null) {
                //找到值
                // supplier might be a Factory or a CacheValue<V> instance
                //如果supplier不為空,則直接取出supplier中的值返回
                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)

            //準備創(chuàng)建
            // 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);
                }
            }
        }
    }

Invocationhandler接口類

//每一個代理類實例有一個相對應的InvocationHandler句柄,一旦代理類實例調用了其中一個method方法都會觸發(fā)該句柄的invoke方法,并將相關信息返回
//1 proxy 代理類實例
//2 method ,調用的方法,
//3 args   該用法的參數
public interface InvocationHandler {
    
 public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

ProxyClassFactory 最終類通過反射來生成代理類

  @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
            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.
             */
            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 the default package.
                proxyPkg = "";
            }

            {
                // Android-changed: Generate the proxy directly instead of calling
                // through to ProxyGenerator.
                List<Method> methods = getMethods(interfaces);
                Collections.sort(methods, ORDER_BY_SIGNATURE_AND_SUBTYPE);
                validateReturnTypes(methods);
                List<Class<?>[]> exceptions = deduplicateAndGetExceptions(methods);

                Method[] methodsArray = methods.toArray(new Method[methods.size()]);
                Class<?>[][] exceptionsArray = exceptions.toArray(new Class<?>[exceptions.size()][]);

                /*
                 * Choose a name for the proxy class to generate.
                 */
                long num = nextUniqueNumber.getAndIncrement();
                String proxyName = proxyPkg + proxyClassNamePrefix + num;

                return generateProxy(proxyName, interfaces, loader, methodsArray,
                                     exceptionsArray);
            }
        }
    }

說了半天的動態(tài)代理,那么動態(tài)代理到底生成的類長啥樣呢,我們如何才可以看到呢?

查詢動態(tài)代理類

  1. 新建一個java module
  2. 將上述抽象接口和真實角色的class 考入java module中
  3. 新建一個ProxyUtils類
 public static void generateClassFile(Class clazz,String proxyName)
    {
        byte[] classFile = ProxyGenerator.generateProxyClass(proxyName, clazz.getInterfaces());
        String path = clazz.getResource(".").getPath();
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(path + proxyName + ".class");
            fos.write(classFile);
            fos.flush();
        }catch(Exception e) {
            e.printStackTrace();
        }finally {
            try {
                fos.close();
            }catch(IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        Subject subject=new Subject();//此處為真實角色的對象
        ProxyInterface proxy =(ProxyInterface) Proxy.newProxyInstance(subject.getClass().getClassLoader(),
                new Class[]{ProxyInterface.class},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
                        return null;
                    }
                });
        generateClassFile(Subject.class,"SubjectProxy");//第二個參數是生成的代理類名
        proxy.confirmAction();
    }
  1. 運行ProxyUtils類的Main函數,則會在build/classes/java/main/com/XXX/proxylib/目錄下生成 SubjecProxy.class文件
  2. 分析一下這個class文件

    1.他是一個最終類,并且繼承Proxy ,實現了我們自己的抽象接口ProxyInterface
    2.有一個public 修飾的構造函數,并且InvocationHandler作為參數
    3.重寫了Object的equals()、toString()、hasCode()方法
    4.實現了抽象接口ProxyInterface的方法,方法體是使用InvocationHandler實例來調用它的invoke方法,并將代理實例、方法、參數作為參數回調回去
    5.靜態(tài)代碼區(qū):通過反射實現類名和方法的反射,并將反射的method存放到靜態(tài)變量中

實際運行生成的代碼如下:

public final class SubjectProxy extends Proxy implements ProxyInterface {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public SubjectProxy(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 confirmAction() 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);
        } 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.leon.proxylib.ProxyInterface").getMethod("confirmAction");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

到此為止,已經將java的動態(tài)代理,代理模式包括相關源碼的剖析都已經介紹完了,有需要的同學的可以自己跟著上面的額內容實操一下加強記憶。

知識點總結:

1、設計模式---代理模式

2、調用處理器InvocationHandler

3、Java反射機制

4、Map緩存機制

今天就學習到這里了,歡迎來踩點

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容