要想了解Java動(dòng)態(tài)代理,首先要了解什么叫做代理,熟悉設(shè)計(jì)模式的朋友一定知道在Gof總結(jié)的23種設(shè)計(jì)模式中,有一種叫做代理(Proxy)的對(duì)象結(jié)構(gòu)型模式,動(dòng)態(tài)代理中的代理,指的就是這種設(shè)計(jì)模式。

在我看來(lái)所謂的代理模式,和23種設(shè)計(jì)模式中的“裝飾模式”是一個(gè)東西。23種設(shè)計(jì)模式中將它們作為兩種模式,網(wǎng)上也有些文章講這兩種模式的異同,從細(xì)節(jié)來(lái)看,確實(shí)可以人為地區(qū)分這兩種模式,但是抽象到一定高度后,我認(rèn)為這兩種模式是完全一樣的。因此學(xué)會(huì)了代理模式,也就同時(shí)掌握了裝飾模式。

代理模式

代理模式簡(jiǎn)單來(lái)說(shuō),就是對(duì)一個(gè)對(duì)象進(jìn)行包裝,包裝后生成的對(duì)象具有和原對(duì)象一樣的方法列表,但是每個(gè)方法都可以是被包裝過(guò)的。

靜態(tài)代理

讓我們先來(lái)看一段代碼:

萬(wàn)碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開(kāi)發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

package common;public class Test {    static interface Subject{        void sayHi();        void sayHello();
    }    
    static class SubjectImpl implements Subject{

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

        @Override        public void sayHello() {
            System.out.println("hello");
        }
    }    
    static class SubjectImplProxy implements Subject{        private Subject target;        
        public SubjectImplProxy(Subject target) {            this.target=target;
        }
        
        @Override        public void sayHi() {
            System.out.print("say:");
            target.sayHi();
        }

        @Override        public void sayHello() {
            System.out.print("say:");
            target.sayHello();
        }
    }    
    public static void main(String[] args) {
        Subject subject=new SubjectImpl();
        Subject subjectProxy=new SubjectImplProxy(subject);
        subjectProxy.sayHi();
        subjectProxy.sayHello();
    }
}

萬(wàn)碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開(kāi)發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

 

這段代碼中首先定義了一個(gè)Subject接口,接口中有兩個(gè)方法。

然后定義了SubjectImpl類實(shí)現(xiàn)Subject接口并實(shí)現(xiàn)其中的兩個(gè)方法,到這里肯定是沒(méi)問(wèn)題的。

現(xiàn)在再定義一個(gè)SubjuectImplProxy類,也實(shí)現(xiàn)Subject接口。這個(gè)SubjectImplProxy類的作用是包裝SubjectImpl類的實(shí)例,它的內(nèi)部定義一個(gè)變量target來(lái)保存一個(gè)SubjectImpl的實(shí)例。SubjectImplProxy也實(shí)現(xiàn)了接口規(guī)定的兩個(gè)方法,并且在它的實(shí)現(xiàn)版本中,都調(diào)用了SubjectImpl的實(shí)現(xiàn),但是又添加了自己的處理邏輯。

相信這段代碼不難理解,它通過(guò)對(duì)SubjectImpl進(jìn)行包裝,達(dá)到了給輸出內(nèi)容添加前綴的功能。這種代理方式叫做靜態(tài)代理。

動(dòng)態(tài)代理

從上面的演示中我們不難看出靜態(tài)代理的缺點(diǎn):我們對(duì)SubjectImpl的兩個(gè)方法,是進(jìn)行的相同的包裝,但是卻要在SubjectImplProxy里把相同的包裝邏輯寫兩次,而且以后如果Subject接口再添加新的方法,SubjectImplProxy也必須要添加新的實(shí)現(xiàn),盡管SubjectImplProxy對(duì)所有方法的包裝可能都是一樣的。

下面我把上面例子的靜態(tài)代理改成動(dòng)態(tài)代理,我們來(lái)看一下區(qū)別:

萬(wàn)碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開(kāi)發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

package common;import java.lang.invoke.MethodHandle;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class Test {    static interface Subject{        void sayHi();        void sayHello();
    }    
    static class SubjectImpl implements Subject{

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

        @Override        public void sayHello() {
            System.out.println("hello");
        }
    }    
    static class ProxyInvocationHandler implements InvocationHandler{        private Subject target;        public ProxyInvocationHandler(Subject target) {            this.target=target;
        }

        @Override        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.print("say:");            return method.invoke(target, args);
        }
        
    }    
    public static void main(String[] args) {
        Subject subject=new SubjectImpl();
        Subject subjectProxy=(Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), new ProxyInvocationHandler(subject));
        subjectProxy.sayHi();
        subjectProxy.sayHello();
        
    }
}

萬(wàn)碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開(kāi)發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

 

只看main方法的話,只有第二行和之前的靜態(tài)代理不同,同樣是生成一個(gè)subjectProxy代理對(duì)象,只是生成的代碼不同了。靜態(tài)代理是直接new 一個(gè)SubjectImplProxy的實(shí)例,而動(dòng)態(tài)代理則調(diào)用了java.lang.reflect.Proxy.newProxyInstance()方法,我們來(lái)看一下這個(gè)方法的源碼:

萬(wàn)碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開(kāi)發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)        throws IllegalArgumentException
    {        if (h == null) {            throw new NullPointerException();
        }        /*
         * Look up or generate the designated proxy class.         */
        Class<?> cl = getProxyClass(loader, interfaces);  //獲取代理類的Class        /*
         * Invoke its constructor with the designated invocation handler.         */
        try {
            Constructor cons = cl.getConstructor(constructorParams);  //constructorParams是寫死的:{ InvocationHandler.class },上邊返回的代理類Class一定是extends Proxy的,而Proxy有一個(gè)參數(shù)為InvocationHandler的構(gòu)造函數(shù)            return cons.newInstance(new Object[] { h });  //這里通過(guò)構(gòu)造函數(shù)將我們自己定義的InvocationHandler的子類傳到代理類的實(shí)例里,當(dāng)我們調(diào)用代理類的任何方法時(shí),實(shí)際上都會(huì)調(diào)用我們定義的InvocationHandler子類重寫的invoke()函數(shù)
        } catch (NoSuchMethodException e) {            throw new InternalError(e.toString());
        } catch (IllegalAccessException e) {            throw new InternalError(e.toString());
        } catch (InstantiationException e) {            throw new InternalError(e.toString());
        } catch (InvocationTargetException e) {            throw new InternalError(e.toString());
        }
    }

萬(wàn)碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開(kāi)發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

上面的 Class<?> cl = getProxyClass(loader, interfaces);  調(diào)用的getProxyClass方法:

萬(wàn)碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開(kāi)發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

  Class<?><?> (interfaces.length > 65535  IllegalArgumentException("interface limit exceeded"<?> proxyClass = = 
        Set<Class<?>> interfaceSet =  HashSet<> ( i = 0; i < interfaces.length; i++=<?> interfaceClass = = Class.forName(interfaceName,  (interfaceClass != + " is not visible from class loader"
             (! + " is not an interface"
             "repeated interface: " +=<String> key =<List<String>, Object>= (cache == =  HashMap<>
        
            = (value = (Class<?> (proxyClass != 
                      (value ==