java代理模式的那些事

java代理模式-登場

什么是代理模式?

代理模式是java中的一種設(shè)計模式,它其實就是設(shè)置一個中間環(huán)節(jié)來代理你要對原目標(biāo)對象的訪問。簡言之,代理模式就是有一個充當(dāng)代理者身份的類或方法來控制原對象的引用。

還是不太理解,你能舉個例子說明一下嗎?

這里一個很好的例子(引用鏈接): 一個公司是賣攝像頭的,但公司不直接跟用戶打交道,而是通過代理商跟用戶打交道。如果:公司接口中有一個賣產(chǎn)品的方法,那么公司需要實現(xiàn)這個方法,而代理商也必須實現(xiàn)這個方法。如果公司賣多少錢,代理商也賣多少錢,那么代理商就賺不了錢。所以代理商在調(diào)用公司的賣方法后,加上自己的利潤然后再把產(chǎn)品賣給客戶。而客戶不直接跟公司打交道,或者客戶根本不知道公司的存在,然而客戶最終卻買到了產(chǎn)品。*

它用途應(yīng)該挺大的吧?

它是java中常用的設(shè)計模式之一,并且在spring框架中有廣泛應(yīng)用(AOP),它一般有三種模式:靜態(tài)代理、jdk1.6+中的動態(tài)代理、cglib動態(tài)代理。

java代理模式-靜態(tài)代理

首先,動態(tài)代理是通過反射機制來處理相關(guān)業(yè)務(wù)方法。那么你也猜到了,靜態(tài)代理則是通過編寫代理類來完成代理過程。

公司要賣的產(chǎn)品(實現(xiàn)接口)

package top.code666.porxy;
//公司中的業(yè)務(wù)接口
public interface ICompany {
    //賣攝像頭
    void sellCamera();
} 

公司的實現(xiàn)類(目標(biāo)類)

package top.code666.porxy;
//公司類
public class CompanyImpl implements ICompany{
    @Override
    public void sellCamera() {
        System.out.println("一個很好的Camera,高清無碼。(xx公司生產(chǎn)Camera并出售給代理商,定價¥1000)");
    }
}

代理商的實現(xiàn)類(代理類)

package top.code666.porxy;

//代理類
public class ProxyImpl implements ICompany{
    private CompanyImpl ci;
    
    public ProxyImpl(CompanyImpl ci){
        this.ci = ci;
    }
    
    @Override
    public void sellCamera() {
        System.out.println("(賣前打了一波廣告)ss微商成為xx公司的最大代理商,現(xiàn)在正式售賣Camera了!不要998,不要98,只要9998你就可以把它帶回家~");
        ci.sellCamera();
        System.out.println("(賣后服務(wù))恭喜你購買成功,三分鐘內(nèi)無條件退換哦~");
    }

}

客戶(測試類)

package top.code666.porxy;

//一個有錢的人
public class RichMan {
    public static void main(String[] args) {
        CompanyImpl ci = new CompanyImpl();
        ProxyImpl pi = new ProxyImpl(ci);
        pi.sellCamera();
    }
}

運行結(jié)果

(賣前打了一波廣告)ss微商成為xx公司的最大代理商,現(xiàn)在正式售賣Camera了!不要998,不要98,只要9998你就可以把它帶回家~
一個很好的Camera,高清無碼。(xx公司生產(chǎn)Camera并出售給代理商,定價¥1000)
(賣后服務(wù))恭喜你購買成功,三分鐘內(nèi)無條件退換哦~

你現(xiàn)在也感覺到了,靜態(tài)代理就是這么的簡單。不過它卻存在許多弊端:

  1. 如果要代理的產(chǎn)品多了,那么你的整個項目將變得非常冗余。
  2. 如果要修改接口,那么你的目標(biāo)對象和代理對象都要修改,所以不易于維護。

這時我們的動態(tài)代理就可以很好的解決這些弊端了。燈登瞪等~ ,讓我們歡迎動態(tài)代理閃亮登場吧(前方高能,如果你對java的反射機制不是很了解,請先去學(xué)習(xí)反射相關(guān)知識后再過來)

java代理模式-動態(tài)代理(JDK1.6+)

JDK1.6以上的版本自帶了動態(tài)代理方法。我們只需要實現(xiàn)InvocationHandler就可以使用代理了。前提:你的目標(biāo)對象必須實現(xiàn)接口,否則不能使用JDK動態(tài)代理

接口與目標(biāo)類是與上一樣,所以這里不再重復(fù)

代理商的實現(xiàn)類(代理類)

package top.code666.jdkproxy;

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

//代理對象
public class ProxyImpl implements InvocationHandler{
    //目標(biāo)對象
    private Object target;
    
    public ProxyImpl(Object target){
        this.target = target;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("(賣前打了一波廣告)ss微商成為xx公司的最大代理商,現(xiàn)在正式售賣Camera了!不要998,不要98,只要9998你就可以把它帶回家~");
        Object result = method.invoke(target, args); // 調(diào)用目標(biāo)類的方法
        System.out.println("(賣后服務(wù))恭喜你購買成功,三分鐘內(nèi)無條件退換哦~");
        return result;
    }

}

客戶(測試類)

package top.code666.jdkproxy;

import java.lang.reflect.Proxy;

//一個有錢的人
public class RichMan {
    public static void main(String[] args) {
        ICompany ic = new CompanyImpl();
        ProxyImpl pi = new ProxyImpl(ic); //傳入目標(biāo)對象
        ICompany proxySubject = (ICompany) Proxy.newProxyInstance(
                CompanyImpl.class.getClassLoader(), 
                CompanyImpl.class.getInterfaces(), pi); // new代理實例
        proxySubject.sellCamera();
    }
}

可以發(fā)現(xiàn),在JDK的動態(tài)代理中,我們的代理類是不需要再重復(fù)寫了的,可以共用同一個。但是它需要定義接口,然后才能實現(xiàn)代理功能,所以還是存在一定的局限性。下面讓我們來看看CGLIB是怎么實現(xiàn)代理功能的吧,它會不會不需要就能實現(xiàn)呢?

java代理模式-動態(tài)代理(cglib.jar)

cglib可以在目標(biāo)類不實現(xiàn)接口方法時,也能夠給這樣的類提供動態(tài)代理。而JDK中是不行的。Spring在給某個類提供動態(tài)代理時會自動在JDK動態(tài)代理和CGLIB動態(tài)代理中動態(tài)的選擇。(其實JDK中的動態(tài)代理是要比CGLIB中動態(tài)代理效率要稍微高一點點的)

使用cglib需要導(dǎo)入cglib.jar+asm.jar

目標(biāo)類(因為是直接復(fù)制的,類名啥的我就沒改了,Impl或許對一些人來說有點礙眼……)

package top.code666.cglib;

//公司類
public class CompanyImpl{
    
    public void sellCamera() {
        System.out.println("一個很好的Camera,高清無碼。(xx公司生產(chǎn)Camera并出售給代理商,定價¥1000)");
    }

}

代理類

package top.code666.cglib;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

//代理對象
public class ProxyImpl implements MethodInterceptor{

    @Override
    public Object intercept(Object arg0, Method arg1, Object[] arg2,
            MethodProxy arg3) throws Throwable {
        System.out.println("(賣前打了一波廣告)ss微商成為xx公司的最大代理商,現(xiàn)在正式售賣Camera了!不要998,不要98,只要9998你就可以把它帶回家~");
        Object result = arg3.invokeSuper(arg0, arg2);
        System.out.println("(賣后服務(wù))恭喜你購買成功,三分鐘內(nèi)無條件退換哦~");
        return result;
    }
}

測試類

package top.code666.cglib;

import net.sf.cglib.proxy.Enhancer;

//一個有錢的人
public class RichMan {
    public static void  main(String ... args) {
        CompanyImpl target = new CompanyImpl();
        RichMan test = new RichMan();
        CompanyImpl proxyTarget = (CompanyImpl) test.createProxy(CompanyImpl.class);
        proxyTarget.sellCamera();
    }

    public Object createProxy(Class targetClass) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(targetClass);
        enhancer.setCallback(new ProxyImpl());
        return enhancer.create();
    }
}

JDK動態(tài)代理與CGLib動態(tài)代理均是實現(xiàn)Spring AOP的基礎(chǔ)。

代理對象的生成過程由Enhancer類實現(xiàn),大概步驟如下:

  1. 生成代理類Class的二進制字節(jié)碼;
  2. 通過Class.forName加載二進制字節(jié)碼,生成Class對象;
  3. 通過反射機制獲取實例構(gòu)造,并初始化代理類對象。

總結(jié)

  1. 靜態(tài)代理實現(xiàn)較簡單,只要代理對象對目標(biāo)對象進行包裝,即可實現(xiàn)增強功能,但靜態(tài)代理只能為一個目標(biāo)對象服務(wù),如果目標(biāo)對象過多,則會產(chǎn)生很多代理類。
  2. JDK動態(tài)代理需要目標(biāo)對象實現(xiàn)業(yè)務(wù)接口,代理類只需實現(xiàn)InvocationHandler接口。
  3. 動態(tài)代理生成的類為 class com.sun.proxy.$Proxy4,cglib代理生成的類為class com.cglib.UserDao$$EnhancerByCGLIB$$552188b6。
  4. 靜態(tài)代理在編譯時產(chǎn)生class字節(jié)碼文件,可以直接使用,效率高。
  5. 動態(tài)代理必須實現(xiàn)InvocationHandler接口,通過反射代理方法,比較消耗系統(tǒng)性能,但可以減少代理類的數(shù)量,使用更靈活。
  6. cglib代理無需實現(xiàn)接口,通過生成類字節(jié)碼實現(xiàn)代理,比反射稍快,不存在性能問題,但cglib會繼承目標(biāo)對象,需要重寫方法,所以目標(biāo)對象不能為final類。

覺得還行可以點個星星哦

最后編輯于
?著作權(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)容

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