Java之動態(tài)代理

靜態(tài)代理

先看一個例子,有個汽車記錄功能,我們既要記錄行駛的時間,又要記錄其它日志,如果這些事全部交給Car這個對象來做,那么它要處理的事情就太多了,既要跑還有寫,所以為了給Car減輕負擔(dān),代理類就誕生了,代碼如下:

//接口類
public interface MoveAble {
    void move();
}

//Car類
public class Car implements MoveAble {
    @Override
    public void move() {
        System.out.println("car move中");
    }
}

//記錄時間代理類
public class TimeProxy implements MoveAble{
    private MoveAble m;
    public TimeProxy(MoveAble m){
        this.m = m;
    }

    @Override
    public void move() {
        System.out.println("time開始");
        m.move();
        System.out.println("time結(jié)束");
    }
}

//記錄日志代理類
public class LogProxy implements MoveAble {
    private MoveAble m;
    public LogProxy(MoveAble m) {
        this.m = m;
    }

    @Override
    public void move() {
        System.out.println("log開始");
        m.move();
        System.out.println("log結(jié)束");
    }
}

從以上的代碼中可以看出我們創(chuàng)造了兩個新的類去繼承和Car一樣的接口,這樣做本質(zhì)上來說它們?nèi)齻€就都有跑的方法了。那么我們可以像下面的代碼那樣調(diào)用它們:

public static void main(String[] args) {
        Car car = new Car();
        LogProxy logProxy = new LogProxy(car);
        TimeProxy timeProxy = new TimeProxy(logProxy);
        timeProxy.move();
}

上面這段代碼中把Car先放進了LogProxy里,又把LogProxy放進了TimeProxy里,接著調(diào)用了TimeProxy對象的move方法,這時候會一層一層的調(diào)用move方法,從TimeProxy到LogProxy再到Car,這樣子就把記錄時間和日志的功能給完成了。
那么這種代理模式有什么好處呢,可以讓不同的功能做不同的事,也就是單一職責(zé),拆分了代碼邏輯,使邏輯更加的清晰了。
但是這種靜態(tài)的代理有個問題,就是我們需要自己去寫代理類,如果功能少還好說,要是功能多的話就會很麻煩,所以,動態(tài)代理就開始登場了。

動態(tài)代理

jdk中為我們提供了創(chuàng)建動態(tài)代理對象的方式,即Proxy類。
Proxy類位于java.lang.reflect包中,包含有一個靜態(tài)的創(chuàng)建方法newProxyInstance,代碼如下:

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException{
       ......//暫時可以忽略的邏輯
}

在上面的創(chuàng)建Proxy類代碼中需要接口三個參數(shù):

  • ClassLoader loader
    類加載器
  • Class<?>[] interfaces
    類實現(xiàn)的接口
  • InvocationHandler h 處理器

我們重點看一下第三個參數(shù)InvocationHandler,這個類是個接口,里面只有一個invoke方法,我們所要做的處理邏輯都會在這個方法里執(zhí)行。代碼如下:

public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;

invoke方法里也有三個參數(shù):

  • Object proxy 調(diào)用invoke方法的對象實例
  • Method method 對象的方法
  • Object[] args 參數(shù)

那么我們應(yīng)該怎么使用動態(tài)代理創(chuàng)建我們的代理類呢,基于上面Car記錄時間和日志的例子,我們可以這樣寫:

public static void main(String[] args) {
        Car car = new Car();
        MoveAble m = (MoveAble) Proxy.newProxyInstance(car.getClass().getClassLoader(), car.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                System.out.println("log開始");
                System.out.println("time開始");

                method.invoke(car);

                System.out.println("time結(jié)束");
                System.out.println("log結(jié)束");

                return null;
            }
        });
        m.move();
}

是不是很簡單,有個動態(tài)代理,我們就不用自己去創(chuàng)建LogProxy,TimeProxy等等代理類了。

動態(tài)代理原理

知道了通過動態(tài)代理的方式可以幫我們自動的生成一些代理類,那么Proxy究竟是怎么辦到的呢,下面就開始分析一下Proxy的內(nèi)部原理。首先,看一下newProxyInstance這個靜態(tài)方法,代碼如下:

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        
        Class<?> cl = getProxyClass0(loader, intfs); //1

        try {
            final Constructor<?> cons = cl.getConstructor(constructorParams);//2
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                cons.setAccessible(true);
                AccessController.doPrivileged call.
            }
            return cons.newInstance(new Object[]{h});//3
        } 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);
        }
    }

為了邏輯清晰我刪除了注釋,這段代碼的主要邏輯就是在1,2,3處,這三步分別是:

  • 1.得到Proxy的Class對象
  • 2.通過Class對象得到Constructor對象
  • 3.通過Constructor對象的newInstance方法獲得實例對象

下面我們主要分析Class<?> cl = getProxyClass0(loader, intfs); //1這行代碼,來看看這個方法是怎么生成的Class類對象的。
這個方法的代碼很簡單:

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);
}

主要就是看最后一行代碼,返回的就是Class對象

public V get(K key, P parameter) {

       ......

        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
        Supplier<V> supplier = valuesMap.get(subKey);
        Factory factory = null;

        while (true) {
            if (supplier != null) {
                // supplier might be a Factory or a CacheValue<V> instance
                V value = supplier.get();
                if (value != null) {
                    return value;//1
                }
            }

            // 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);
                }
            }
        }
    }

這段代碼我刪除了一些注釋和與主要邏輯關(guān)系不大的代碼。
首先來看幾個比較的對象:

  1. Supplier<V> supplier
  2. Factory factory

先看最后返回的value是通過supplier.get()的,也就是代碼中1處的邏輯,那么這個supplier是什么呢,它是個接口,下面的factory是它的實現(xiàn)類,下面的while循環(huán)中就是給supplier賦值的過程。也就是說value的最終獲取是通過factory里的get方法取得,那么我們再看一下這個get方法邏輯:

public synchronized V get() { // serialize access
            ......

            // create new value
            V value = null;
            try {
                value = Objects.requireNonNull(valueFactory.apply(key, parameter));//1
            } finally {
                if (value == null) { // remove us on failure
                    valuesMap.remove(subKey, this);
                }
            }

            return value;
}

這里最重要的邏輯是代碼1處,從valueFactory的apply里得到了value,也就是我們最終要得到的Class對象。那么我們再來看看這個valueFactory是什么。

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

valueFactory是BiFunction接口的實現(xiàn)類對象,WeakCache初始化的時候被傳進來

public class Proxy implements java.io.Serializable {

    private static final long serialVersionUID = -2222568056686623797L;

    /** parameter types of a proxy class constructor */
    private static final Class<?>[] constructorParams =
        { InvocationHandler.class };

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

        ......
}

可以看出來是在Proxy類中被初始化的,也就是ProxyClassFactory類對象。這個ProxyClassFactory也實現(xiàn)了BiFunction,那么上面的apply方法最終的實現(xiàn)邏輯就是在這個類中:

public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

           ......
                
                long num = nextUniqueNumber.getAndIncrement();
                String proxyName = proxyPkg + proxyClassNamePrefix + num;

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

這個方法代碼很長,我們只需要關(guān)注這幾行就行了,可以看出,這個方法設(shè)定了一個規(guī)則,生成一個字符串proxyName也就是我們的代理類的類名,最后的generateProxy方法實際上是調(diào)用了本地的一個方法,就是生成Class對象的方法:

@FastNative
    private static native Class<?> generateProxy(String name, Class<?>[] interfaces,
                                                 ClassLoader loader, Method[] methods,
                                                 Class<?>[][] exceptions);

那么到現(xiàn)在為止就把動態(tài)代理怎么生成代理對象的機制分析完了。

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

相關(guān)閱讀更多精彩內(nèi)容

  • java的動態(tài)代理機制是在運行期間為目標(biāo)對象生成一個代理對象,而將自己格外需要處理的業(yè)務(wù)邏輯進行“插入”,以達到運...
    Sophie12138閱讀 409評論 0 0
  • https://blog.csdn.net/luanlouis/article/details/24589193 ...
    小陳阿飛閱讀 968評論 1 1
  • 《李鴻章傳》是由梁啟超所著的書籍,從李鴻章的早年落拓,寫到他參加鎮(zhèn)壓太平軍、甲午海戰(zhàn),創(chuàng)辦洋務(wù)運動,周旋于世界外交...
    落揚虛虛閱讀 1,361評論 1 4
  • 1.我們那邊的村落跟北方的村落不太一樣,北方那邊的村落都比較大,而且各種姓氏的都有,也有很多同村落結(jié)婚的,而我們那...
    安山1閱讀 182評論 0 1
  • 這個雨蒙蒙的夜,一部電影剛好契合我今晚的心情,慶幸的是,身邊的好友也有此共鳴。????????? 對這部片子沒...
    亦水菲甜閱讀 190評論 0 0

友情鏈接更多精彩內(nèi)容