(八)淺析Java代理模式

1.代理模式
定義:代理模式(Proxy),為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的方法。

Proxy.png

2.應(yīng)用場景
(1)遠(yuǎn)程代理:也就是為一個(gè)對(duì)象在不同的地址空間提供局部代表,這樣可以隱藏一個(gè)對(duì)象存在于不同地址空間的事實(shí),例如,RPC遠(yuǎn)程過程調(diào)用。
(2)虛擬代理:是根據(jù)需要?jiǎng)?chuàng)建開銷很大的對(duì)象。通過它來存放實(shí)例化需要很長時(shí)間的真實(shí)對(duì)象,以達(dá)到性能的最優(yōu)化。
(3)安全代理:用來控制真實(shí)對(duì)象訪問時(shí)的權(quán)限。
(4)智能引用:是指當(dāng)調(diào)用真實(shí)的對(duì)象時(shí),代理處理另外一些事。如計(jì)算真實(shí)對(duì)象的引用次數(shù),這樣當(dāng)該對(duì)象沒有引用時(shí),可以自動(dòng)釋放它。

3.代理類型
下面我們通過一個(gè)購票流程來詳細(xì)說明一下靜態(tài)代理、動(dòng)態(tài)代理和Cglib代理三種代理的優(yōu)缺點(diǎn)。以前沒有12306軟件的時(shí)候,購買火車票只能到售票處購買,現(xiàn)在有了12306軟件以后,可以實(shí)現(xiàn)在線購買火車票的功能了,售票處是真實(shí)實(shí)體,12306軟件就是售票處的代理類。
(1)靜態(tài)代理

package com.nwpu.pattern.proxy.statics;
public interface Subject {
    void buyTicket();
}

package com.nwpu.pattern.proxy.statics;
public class TicketOffice implements Subject {
    @Override
    public void buyTicket() {
        System.out.println("成功買到一張票!");
    }
}

package com.nwpu.pattern.proxy.statics;
public class TicketSoftware implements Subject {
    // 代理類維持一個(gè)真實(shí)實(shí)體的引用
        private TicketOffice office;
    public void setTicketOffice(TicketOffice office) {
        this.office = office;
    }
    @Override
    public void buyTicket() {
        office.buyTicket();
    }
}

package com.nwpu.pattern.proxy.statics;
public class Client {
    public static void main(String[] args) {
        TicketOffice office = new TicketOffice();
        TicketSoftware software = new TicketSoftware();
        software.setTicketOffice(office);
        software.buyTicket();

    }
}

從上述例子我們可以得知,靜態(tài)代理的代理關(guān)系在編譯期間就已確定,適合代理類較少且確定的場景,否則要新建大量的代理類。靜態(tài)代理模式要求目標(biāo)類和代理類要實(shí)現(xiàn)共同的接口,當(dāng)目標(biāo)類中有大量方法時(shí),代理類也需要重寫大量的方法,違背重復(fù)代碼只寫一次原則。此外,當(dāng)接口新增方法時(shí),代理類和目標(biāo)類也需要實(shí)現(xiàn)此方法,增加了代碼的維護(hù)成本。

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

package com.nwpu.pattern.proxy.dynamic;
public interface Subject {
    void buyTicket();
}

package com.nwpu.pattern.proxy.dynamic;
public class TicketOffice implements Subject {
    @Override
    public void buyTicket() {
        System.out.println("成功買到一張票!");
    }
}

package com.nwpu.pattern.proxy.dynamic;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyInvocationHandler implements InvocationHandler {
    private TicketOffice ticketOffice;
    public void setTicketOffice(TicketOffice ticketOffice) {
        this.ticketOffice = ticketOffice;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(ticketOffice, args);
        return result;
    }
}

package com.nwpu.pattern.proxy.dynamic;

import java.lang.reflect.Proxy;

public class Client {
    public static void main(String[] args) {
        TicketOffice office = new TicketOffice();
        MyInvocationHandler handler = new MyInvocationHandler();
        handler.setTicketOffice(office);
        Subject proxy = (Subject) Proxy.newProxyInstance(office.getClass().getClassLoader(),
                office.getClass().getInterfaces(), handler);
        proxy.buyTicket();
    }
}

從上述示例中我們可以得知,利用動(dòng)態(tài)代理可以實(shí)現(xiàn)在運(yùn)行時(shí)創(chuàng)建一個(gè)實(shí)現(xiàn)了一組給定接口的新類,這種功能只有在編譯時(shí)無法確定需要實(shí)現(xiàn)哪個(gè)接口時(shí)才有必要使用到。

(3)Cglib代理
jdk動(dòng)態(tài)代理依賴于接口的實(shí)現(xiàn),而當(dāng)我們只有類沒有接口的時(shí)候就需要使用另一種動(dòng)態(tài)代理技術(shù) Cglib動(dòng)態(tài)代理。Cglib代理是針對(duì)類來實(shí)現(xiàn)代理的,原理是對(duì)指定的目標(biāo)類生成一個(gè)子類并重寫其中業(yè)務(wù)方法來實(shí)現(xiàn)代理
代理類對(duì)象是由 Enhancer 類創(chuàng)建的,Cglib創(chuàng)建動(dòng)態(tài)代理類的模式如下:

  • a.查找目標(biāo)類上的所有非 final 的 public 類型的方法 (final 的不能被重寫);
  • b.將這些方法的定義轉(zhuǎn)成字節(jié)碼;
  • c.將組成的字節(jié)碼轉(zhuǎn)換成相應(yīng)的代理的 Class 對(duì)象然后通過反射獲得代理類的實(shí)例對(duì)象;
  • d.實(shí)現(xiàn) MethodInterceptor 接口, 用來處理對(duì)代理類上所有方法的請(qǐng)求。
package com.nwpu.pattern.proxy.cglib;

public class TicketOffice {
    public void buyTicket() {
        System.out.println("成功買到一張票!");
    }
}

package com.nwpu.pattern.proxy.cglib;

import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class CglibProxy implements MethodInterceptor {
    
    public TicketOffice creatProxyedObj(CglibProxy cglibProxy) {    
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(TicketOffice.class);
        enhancer.setCallback(cglibProxy);
        return (TicketOffice) enhancer.create();
    }

    @Override
    public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        return methodProxy.invokeSuper(object, args);
    }
}

package com.nwpu.pattern.proxy.cglib;

public class Client {
    public static void main(String[] args) {    
        CglibProxy proxy = new CglibProxy();    
        TicketOffice ticketOffice = proxy.creatProxyedObj(proxy);   
        ticketOffice.buyTicket();
    }
}

從上述示例我們可以得知:對(duì)于需要被代理的目標(biāo)類,它只是動(dòng)態(tài)生成一個(gè)子類以覆蓋非 final 的方法,同時(shí)綁定鉤子回調(diào)自定義的攔截器。不同于jdk動(dòng)態(tài)代理,我們不能使用目標(biāo)對(duì)象來創(chuàng)建代理,目標(biāo)對(duì)象只能被 Cglib創(chuàng)建。在示例中,默認(rèn)的無參構(gòu)造方法enhancer.create()被使用來創(chuàng)建目標(biāo)對(duì)象。

原創(chuàng)不易,如需轉(zhuǎn)載,請(qǐng)注明出處@author Davince!

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

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

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