日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

前言

大家好,我是禿頭JAVA人。你們是否在編程中經(jīng)常遇見這樣一個(gè)問題,對(duì)于訪問某個(gè)對(duì)象,我們希望給它的方法前加入一個(gè)標(biāo)記,比如對(duì)象的方法開始執(zhí)行、結(jié)束等等(比如日志記錄)。怎么辦呢,這個(gè)時(shí)候只要我們編寫一個(gè)復(fù)制的類,然后把這個(gè)對(duì)象傳給這個(gè)類,再對(duì)這個(gè)類進(jìn)行操作,不就可以了嗎。這就是代理模式,復(fù)制的類就是代理對(duì)象,通過代理對(duì)象與我們進(jìn)行打交道就可以對(duì)它原來(lái)的對(duì)象進(jìn)行改造。對(duì)于有些時(shí)候現(xiàn)有的對(duì)象不能滿足我們的需求的時(shí)候,如何對(duì)它進(jìn)行擴(kuò)展,對(duì)方法進(jìn)行改造,使其適用于我們所面臨的問題,這就是代理模式的思維出發(fā)點(diǎn)。

目錄

一:代理模式的介紹

二:實(shí)現(xiàn)靜態(tài)代理

三:代理的進(jìn)階:實(shí)現(xiàn)動(dòng)態(tài)代理

四:總結(jié)

接下來(lái)按照目錄,我們來(lái)依次講解本篇博客:

一:代理模式的介紹

1.1:目標(biāo)

為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問

解釋:在實(shí)際編程中我們會(huì)產(chǎn)生一個(gè)代理對(duì)象,然后去引用被代理對(duì)象,對(duì)被代理對(duì)象進(jìn)行控制與訪問,實(shí)現(xiàn)客戶端對(duì)原代理對(duì)象的訪問,詳情見下面的代碼示例。

1.2:適用性

在需要用比較通用和復(fù)雜的對(duì)象指針代替簡(jiǎn)單的指針的時(shí)候,使用Proxy 模式。下面是一 些可以使用Proxy 模式常見情況:
1.2.1:遠(yuǎn)程代理(Remote Proxy )為一個(gè)對(duì)象在不同的地址空間提供局部代表。 NEXTSTEP[Add94] 使用N X P r o x y 類實(shí)現(xiàn)了這一目的。
1.2.2:虛代理(Virtual Proxy )根據(jù)需要?jiǎng)?chuàng)建開銷很大的對(duì)象。在動(dòng)機(jī)一節(jié)描述的I m a g e P r o x y 就是這樣一種代理的例子。
1.2.3: 保護(hù)代理(Protection Proxy )控制對(duì)原始對(duì)象的訪問。保護(hù)代理用于對(duì)象應(yīng)該有不同 的訪問權(quán)限的時(shí)候
1.2.4: 智能指引(Smart Reference )取代了簡(jiǎn)單的指針,它在訪問對(duì)象時(shí)執(zhí)行一些附加操作。

1.3:結(jié)構(gòu)

深度解析Java靜態(tài)代理與動(dòng)態(tài)代理模式的實(shí)現(xiàn)

 

二:實(shí)現(xiàn)靜態(tài)代理

2.1:代碼場(chǎng)景

假如我們現(xiàn)在由以下的場(chǎng)景:文件編輯器要對(duì)一個(gè)圖像文件進(jìn)行操作,遵循以下順序:加載,繪制,獲取長(zhǎng)度和寬度,存儲(chǔ)四個(gè)步驟,但是有個(gè)問題,需要被加載的圖片非常大,每次加載的時(shí)候都要耗費(fèi)很多時(shí)間。并且我們希望對(duì)圖片的操作可以記錄出來(lái)操作的步驟,比如第一步、第二步這樣便于我們?nèi)ダ斫?strong>。為了解決這個(gè)問題,我們可以先考慮解決第一個(gè)問題就是利用代理模式去新建一個(gè)代理對(duì)象,然后在代理對(duì)象里去實(shí)現(xiàn)一個(gè)緩存,這樣下次我們直接可以去緩存里面取對(duì)象,而不用去新建,這樣就省去了新建對(duì)象消耗的資源。另一方面,我們可以考慮去引用原來(lái)的方法,再給這方法基礎(chǔ)上添加我們所要做的記錄。接下里我們用java代碼來(lái)實(shí)現(xiàn)這個(gè)場(chǎng)景:

2.2:代碼示范

2.2.1:首先新建一個(gè)接口,命名為Graphic,其中主要規(guī)范了我們進(jìn)行操作的步驟

public interface Graphic {

    void load();//加載

    void Draw();//繪制

    Extent GetExtent();//獲取長(zhǎng)度和寬度

    void Store();//存儲(chǔ)

}

2.2.2:然后去新建一個(gè)Image類,用于實(shí)現(xiàn)接口,對(duì)操作進(jìn)行具體控制,注意為了其中的Extent是對(duì)寬度和長(zhǎng)度的封裝(省略get和set方法)

public class Image implements Graphic{

    public Image() {
        
        try {
            Thread.sleep(2000); //模擬創(chuàng)建需要花費(fèi)很久的時(shí)間
            System.out.println("正在創(chuàng)建對(duì)象");
        } catch (Exception e) {
            
            e.printStackTrace();
        }
    }
    
    @Override
    public void load() {
        
        System.out.println("進(jìn)行加載..");

    }

    @Override
    public void Draw() {
        
        System.out.println("進(jìn)行繪畫..");
        
    }

    @Override
    public Extent GetExtent() {

        Extent extent = new Extent("100","200");
        
        System.out.println("獲取圖片的屬性是:"+extent.toString());
        
        return extent;
    }



    @Override
    public void Store() {

        System.out.println("圖片進(jìn)行存儲(chǔ)在硬盤里..");
        
    }


}
public class Extent {
    
    private String width;
    
    private String length;

    public Extent(String width, String length) {
        super();
        this.width = width;
        this.length = length;
    }
//getter And setter方法
}

2.2.3:接下來(lái)就是很關(guān)鍵的一步了,新建我們的代理類,我們新建一個(gè)類叫做ImageProxy,然后實(shí)現(xiàn)緩存與記錄的效果:

import java.util.HashMap;
import java.util.Map;

public class ImageProxy  implements Graphic{
    
    private Image image;
    
    private Map<String , Image> cache  = new HashMap<String, Image>();//緩存
    
    public ImageProxy() {
        
        init();
    }
    
    public void init(){ //只需要初始化一次
        
        if (image==null) {
            
             image= new Image();
             
             cache.put("image", image);//放入緩存
            
        }else{

           image=cache.get("image");
    }
    

    @Override
    public void load() {
        
         System.out.println("---第一步開始---");
         
          image.load();
         
         System.out.println("---第一步結(jié)束---");
    }
    
    @Override
    public void Draw() {
        
        System.out.println("---第二步開始---");
        
        image.Draw();
        
        System.out.println("---第二步結(jié)束---");
    }

    @Override
    public Extent GetExtent() {
        
        System.out.println("---第三步開始---");
        Extent extent = image.GetExtent();
        System.out.println("---第三步結(jié)束--");
        
        return extent;
        
    }
    @Override
    public void Store() {
        System.out.println("---第四步開始---");
        
        image.Store();
        
        System.out.println("---第四步結(jié)束--");
        
    }
    
}

2.2.4:我們的文檔編輯器現(xiàn)在要開始進(jìn)行文檔編輯了,我們來(lái)實(shí)現(xiàn)具體的代碼,我們先來(lái)引用一下原對(duì)象,看一下原來(lái)的對(duì)象會(huì)出現(xiàn)什么情況:

public class DocumentEditor {
    
    public static void main(String[] args) {
        
         Graphic proxy = new Image();//引用代碼
         
         proxy.load();
         
         proxy.Draw();
         
         proxy.GetExtent();
         
         proxy.Store();
        
    }

}

2.2.5:測(cè)試代碼

正在創(chuàng)建對(duì)象
進(jìn)行加載..
進(jìn)行繪畫..
獲取圖片的屬性是:Extent [width=100, length=200]
圖片進(jìn)行存儲(chǔ)在硬盤里..

我們可以看出,它會(huì)消耗3秒才會(huì)出來(lái)具體的對(duì)象,并且沒有我們所需要的記錄。好了,我們把2.2.4的引用代碼改為: Graphic proxy = new ImageProxy();

我們?cè)賮?lái)測(cè)試一下:

正在創(chuàng)建對(duì)象
---第一步開始---
進(jìn)行加載..
---第一步結(jié)束---
---第二步開始---
進(jìn)行繪畫..
---第二步結(jié)束---
---第三步開始---
獲取圖片的屬性是:Extent [width=100, length=200]
---第三步結(jié)束--
---第四步開始---
圖片進(jìn)行存儲(chǔ)在硬盤里..
---第四步結(jié)束--

很明顯可以看出,通過訪問我們的代理對(duì)象,就可以實(shí)現(xiàn)對(duì)原方法的改造,這就是代理模式的精髓思想。不過到這里你可能會(huì)問,為什么不對(duì)原對(duì)象進(jìn)行改造呢?為什么要給他新建一個(gè)代理對(duì)象,這不是很麻煩嗎。回答這個(gè)問題,首先要提一個(gè)代碼的設(shè)計(jì)原則,也就是有名的開閉原則:對(duì)擴(kuò)展開放,對(duì)修改關(guān)閉。這句話的意思就是不建議對(duì)原有的代碼進(jìn)行修改,我們要做的事就是盡量不用動(dòng)原有的類和對(duì)象,在它的基礎(chǔ)上去改造,而不是直接去修改它。至于這個(gè)原則為什么這樣,我想其中一個(gè)原因就是因?yàn)檐浖w系中牽一發(fā)很動(dòng)全身的事情很常見,很可能你修改了這一小塊,然而與此相關(guān)的很多東西就會(huì)發(fā)生變化。所以輕易不要修改,而是擴(kuò)展。

三:實(shí)現(xiàn)動(dòng)態(tài)代理

3.1:靜態(tài)代理的不足:

通過看靜態(tài)代理可以動(dòng)態(tài)擴(kuò)展我們的對(duì)象,但是有個(gè)問題,在我們進(jìn)行方法擴(kuò)展的時(shí)候,比如我們的日志功能:每個(gè)前面都得寫第一步、第二步。如果我們要再一些其他的東西,比如權(quán)限校驗(yàn)、代碼說明,一個(gè)兩個(gè)方法還好,萬(wàn)一方法成百個(gè)呢,那我們豈不是要累死。這就是動(dòng)態(tài)代理要解決的問題,只需要寫一次就可以,究竟是怎么實(shí)現(xiàn)的呢,接下里我們來(lái)一探究竟吧。

3.2:動(dòng)態(tài)代理的準(zhǔn)備:

動(dòng)態(tài)代理需要用到JDk的Proxy類,通過它的newProxyInstance()方法可以生成一個(gè)代理類,我們來(lái)通過jdk看一下具體的說明,如何使用它:

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

返回一個(gè)指定接口的代理類實(shí)例,該接口可以將方法調(diào)用指派到指定的調(diào)用處理程序。此方法相當(dāng)于:

   Proxy.getProxyClass(loader, interfaces).
         getConstructor(new Class[] { InvocationHandler.class }).
         newInstance(new Object[] { handler });
 

Proxy.newProxyInstance 拋出 IllegalArgumentException,原因與 Proxy.getProxyClass 相同。

參數(shù):loader - 定義代理類的類加載器interfaces - 代理類要實(shí)現(xiàn)的接口列表h - 指派方法調(diào)用的調(diào)用處理程序返回:一個(gè)帶有代理類的指定調(diào)用處理程序的代理實(shí)例,它由指定的類加載器定義,并實(shí)現(xiàn)指定的接口拋出:IllegalArgumentException - 如果違反傳遞到 getProxyClass 的參數(shù)上的任何限制NullPointerException - 如果 interfaces 數(shù)組參數(shù)或其任何元素為 null,或如果調(diào)用處理程序 h 為 null

從中可以看出它有三個(gè)參數(shù),分別是classlcoder、interface、InvocationHandler.只要我們把這三個(gè)參數(shù)傳遞給他,它就可以 返回給我們一個(gè)代理對(duì)象,訪問這個(gè)代理對(duì)象就可以實(shí)現(xiàn)對(duì)原對(duì)象的擴(kuò)展。接下來(lái),我們用代碼來(lái)實(shí)現(xiàn)它。

3.3:代碼場(chǎng)景

我們來(lái)做這樣一個(gè)場(chǎng)景,我們實(shí)現(xiàn)一個(gè)計(jì)算器,計(jì)算器里面有加減乘除方法,然后我們實(shí)現(xiàn)這個(gè)計(jì)算的接口,有具體的類和被代理的類,我們通過動(dòng)態(tài)代理來(lái)生成代理類,而不用自己去建了,好了,看接下來(lái)的代碼:

3.4:動(dòng)態(tài)代理的代碼實(shí)現(xiàn)

3.4.1:首先我們新建一個(gè)接口,命名為Calculator ,聲明四個(gè)方法:

public interface Calculator {
    
    int add(int i,int j);//加
    
    int sub(int i,int j);//減
    
    int mul(int i,int j);//乘
    
    double div(int i,int j);//除

}

3.4.2:新建一個(gè)實(shí)現(xiàn)類,命名為CalculatorImpl ,也就是被代理類

public class CalculatorImpl  implements Calculator{

    @Override
    public int add(int i, int j) {
        
        return i+j;
    }

    @Override
    public int sub(int i, int j) {
        
        return i-j;
    }

    @Override
    public int mul(int i, int j) {
        
        return i*j;
    }

    @Override
    public double div(int i, int j) {
        
    return  (i/j);
    
    }

}

3.4.3:新建一個(gè)類,命名為CalCulatorDynamicProxy,也就是我們的代理類,用來(lái)對(duì)上面的類進(jìn)行代理:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

public class CalCulatorDynamicProxy { //動(dòng)態(tài)代理類

    private Calculator calculator;//要代理的對(duì)象

    public CalCulatorDynamicProxy(Calculator calculator) {

        this.calculator = calculator;
    }

    public Calculator getCalculator() {

        Calculator proxy = null;

        ClassLoader loader =calculator.getClass().getClassLoader();//獲取類加載器

        Class[] interfaces = new Class[]{Calculator.class};//代理對(duì)象的類型

        InvocationHandler h = new InvocationHandler() {//調(diào)用處理器

        //proxy:正在返回的代理對(duì)象
        //method:被調(diào)用的方法
        //args:傳入的參數(shù)    
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("---日志記錄開始---");
                String name = method.getName();//獲取方法的名字
                System.out.println("方法"+name+"()開始執(zhí)行了");
                System.out.println("方法中的參數(shù)是:"+Arrays.asList(args));
                Object result = method.invoke(calculator, args);
                System.out.println("方法執(zhí)行后的結(jié)果是"+result);
                return result;
            }
        };
        proxy=(Calculator)Proxy.newProxyInstance(loader, interfaces, h);//代理對(duì)象
        return proxy;
    }

}

這里要特別強(qiáng)調(diào)的問題就是:invoke()方法,注意其中的參數(shù),分別是被代理對(duì)象、方法、和對(duì)象參數(shù),這里的原理是反射,通過獲取原對(duì)象的class對(duì)象,然后進(jìn)行處理,我們可以通過method對(duì)象拿到被代理對(duì)象的方法,也是add()、mul()、sub()、div()方法,也可以通過args對(duì)象數(shù)組取得傳入的參數(shù),比如我們具體傳入的數(shù)值,再通過method.invoke()方法進(jìn)行調(diào)用,就進(jìn)行了被代理對(duì)象的方法的執(zhí)行,然后就是返回的結(jié)果(如果方法前為void,返回的就是null)

3.4.4:我們來(lái)做具體的測(cè)試

public class Test {
    
    public static void main(String[] args) {
    
        Calculator cal = new CalculatorImpl();
        
        Calculator proxy = new CalCulatorDynamicProxy(cal).getCalculator();
        
        int add = proxy.add(29, 1);
        
        
        int sub = proxy.sub(9, 2);
        
        
        int mul = proxy.mul(3, 7);
        
        
        double div = proxy.div(6,8);
        
    }

}

具體的測(cè)試結(jié)果:

---日志記錄開始---
方法add()開始執(zhí)行了
方法中的參數(shù)是:[29, 1]
方法執(zhí)行后的結(jié)果是30
---日志記錄開始---
方法sub()開始執(zhí)行了
方法中的參數(shù)是:[9, 2]
方法執(zhí)行后的結(jié)果是7
---日志記錄開始---
方法mul()開始執(zhí)行了
方法中的參數(shù)是:[3, 7]
方法執(zhí)行后的結(jié)果是21
---日志記錄開始---
方法div()開始執(zhí)行了
方法中的參數(shù)是:[6, 8]
方法執(zhí)行后的結(jié)果是0.0

可以看出動(dòng)態(tài)代理模式輕松完成了對(duì)被代理對(duì)象的日志記錄功能,并且只用寫一次,這樣即便有成百上千的方法我們也不怕,這就是動(dòng)態(tài)代理領(lǐng)先于靜態(tài)代理之處,雖然實(shí)現(xiàn)起來(lái)有點(diǎn)麻煩,但是其方便,動(dòng)態(tài)的給被代理對(duì)象添加功能。我們所寫的重復(fù)代碼更少,做的事情更少。

四:總結(jié)

本篇博客介紹了動(dòng)態(tài)代理和靜態(tài)代理的概念,并對(duì)其進(jìn)行了代碼實(shí)現(xiàn),在實(shí)際的工作中,我們會(huì)經(jīng)常遇到需要代理模式的地方,希望能多多思考,促進(jìn)我們形成一定的思維模式。并且動(dòng)態(tài)代理作為SpringAop的實(shí)現(xiàn)原理,封裝了動(dòng)態(tài)代理,讓我們實(shí)現(xiàn)起來(lái)更加方便,對(duì)于這部分內(nèi)容可以只做了解,理解其背后的運(yùn)行機(jī)制即可,并不需要具體實(shí)現(xiàn),如果需要實(shí)現(xiàn),直接使用spring的Aop功能即可。

希望看完本篇,能對(duì)代理這種思維有深入的理解。好了,本篇文章就講到這里,謝謝。

分享到:
標(biāo)簽:靜態(tài) 代理
用戶無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定