溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

java中的代理是指什么

發(fā)布時(shí)間:2020-06-28 14:55:33 來(lái)源:億速云 閱讀:133 作者:元一 欄目:編程語(yǔ)言

本篇文章給大家分享的是有關(guān)java中的代理,小編覺(jué)得挺實(shí)用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說(shuō),跟著小編一起來(lái)看看吧。

代理模式在設(shè)計(jì)模式中的定義就是:為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問(wèn)。說(shuō)白了就是,在一些情況下客戶(hù)不想或者不能直接引用一個(gè)對(duì)象,而代理對(duì)象可以在客戶(hù)和目標(biāo)對(duì)象之間起到中介作用,去掉客戶(hù)不能看到的內(nèi)容和服務(wù)或者增添客戶(hù)需要的額外服務(wù)。

java中的代理是指什么

Java的靜態(tài)代理

舉個(gè)例子,如果我們一些水果,比如:香蕉、蘋(píng)果等,寫(xiě)成Java代碼,大概是下面這個(gè)樣子:

//Fruit.java/**
 * 水果的接口
 */public interface Fruit {    /**
     * 獲取水果的名字
     */
    public String getName();
}//Apple.javapublic class Apple implements Fruit {    @Override
    public String getName() {        return "蘋(píng)果";
    }
}//Banana.javapublic class Banana implements Fruit {    @Override
    public String getName() {        return "香蕉";
    }
}

吃水果,你要削皮吧,你不能每個(gè)水果都寫(xiě)一個(gè)子類(lèi),類(lèi)處理削皮這個(gè)事情吧。因此,我們可以做一個(gè)代理 ,吃蘋(píng)果之前,先把它削皮。 就像下面這樣,把原來(lái)的水果包一層:

//PeelFruitProxy.java/**
 * 代理,讓每個(gè)水果去皮
 */public class PeelFruitProxy implements Fruit {    private Fruit mFruit;    public PeelFruit(Fruit fruit) {        this.mFruit = fruit;
    }    @Override
    public String getName() {
        System.out.println("proxt:" + proxy.getClass().getName());        return "去皮的" + mFruit.getName();
    }
}

添加了測(cè)試類(lèi),測(cè)試類(lèi)如下:

//Main.javapublic class Main {    public static void main(String[] args) {
        Apple apple=new Apple();//原始的蘋(píng)果
        Banana banana=new Banana();//原始的香蕉

        PeelFruitProxy peelApple=new PeelFruitProxy(apple);//代理,添加去皮功能
        PeelFruitProxy peelBanana=new PeelFruitProxy(banana);//代理,添加去皮功能
        System.out.println(peelApple.getName());
        System.out.println(peelBanana.getName());
    }
}

以上就是Java的靜態(tài)代理,簡(jiǎn)單點(diǎn)說(shuō),就是把原來(lái)的目標(biāo)對(duì)象包一層,加入新東西再去調(diào)用目標(biāo)本身。 但是如果只是這樣的靜態(tài)代理,一個(gè)接口,就需要一個(gè)代理,實(shí)現(xiàn)起來(lái)是不是很繁瑣。

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

在Java中,有一個(gè)干這個(gè)事情的類(lèi),叫做Proxy,可以直接使用反射方式,代理攔截。 先簡(jiǎn)單的介紹一下這個(gè)類(lèi),其實(shí)最常用的只有一個(gè)靜態(tài)方法Proxt.newProxyInstance(),是這樣的:

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

首先我們要實(shí)現(xiàn)InvocationHandler,實(shí)現(xiàn)其中的invoke方法,在調(diào)用目標(biāo)對(duì)象的時(shí)候,會(huì)先調(diào)用到invoke方法,需要實(shí)現(xiàn)者在這個(gè)方法中,在主動(dòng)調(diào)用被調(diào)用者方法。

//FruitInvocationHandler.java/**
 * 調(diào)用方法攔截器
 */public class FruitInvocationHandler implements InvocationHandler {    private Fruit mFruit;    public FruitInvocationHandler(Fruit fruit) {        this.mFruit = fruit;
    }    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String result = (String) method.invoke(mFruit, args);//需要在這個(gè)方法里面,主動(dòng)調(diào)用被代理的對(duì)象。
        return "去皮的" + result;
    }
}

運(yùn)行一下:

//Main.Javapublic class Main {    public static void main(String[] args) {
        Apple apple = new Apple();
        Fruit proxyApple = (Fruit) Proxy.newProxyInstance(Fruit.class.getClassLoader(), new Class[]{Fruit.class}, new FruitInvocationHandler(apple));
        System.out.println(proxyApple.getClass().getName());
        System.out.println(proxyApple.getName());

        Banana banana = new Banana();
        Fruit proxyBanana = (Fruit) Proxy.newProxyInstance(Fruit.class.getClassLoader(), new Class[]{Fruit.class}, new FruitInvocationHandler(banana));
        System.out.println(proxyApple.getClass().getName());
        System.out.println(proxyBanana.getName());
    }
}

java中的代理是指什么

這個(gè)方法,就是生成一個(gè)上文中的PeelFruitProxy(當(dāng)然,我們看到的他名字叫:com.sun.proxy.$Proxy0),動(dòng)態(tài)的生成,避免每次都需要寫(xiě),這個(gè)也是叫他動(dòng)態(tài)代理的原因,因?yàn)槲覀兛梢栽谶\(yùn)行時(shí)代理任意類(lèi)。 很多程序中的AOP就是這樣實(shí)現(xiàn)的,但是我們發(fā)現(xiàn)一些特點(diǎn),newProxyInstance()的第二個(gè)參數(shù),是一個(gè)interfaces的列表,為啥要有這個(gè)這個(gè)列表呢?

因?yàn)槲覀儎?dòng)態(tài)生成的代理類(lèi),也需要實(shí)現(xiàn)接口,這樣才方便向下轉(zhuǎn)型,使用其中的方法,不然,生成的類(lèi),類(lèi)名就是com.sun.proxy.$Proxy0這種,并且是在內(nèi)存中,無(wú)法調(diào)用生成的方法。 ** 所以,這種動(dòng)態(tài)代理的方法,有一個(gè)致命的缺點(diǎn),那就是被代理的類(lèi),必須要實(shí)現(xiàn)接口。**

CGLib代理

cglib is a powerful, high performance and quality Code Generation Library, It is used to extend JAVA classes and implements interfaces at runtime.

另一個(gè)大名鼎鼎的Java代理實(shí)現(xiàn),就是CGLib(Code Generation Library),一個(gè)基于ASM的代碼生成框架,可以用他來(lái)動(dòng)態(tài)生成類(lèi),然后實(shí)現(xiàn)對(duì)方法的攔截,就可以避開(kāi)JDK的動(dòng)態(tài)代理, 必須要目標(biāo)類(lèi)實(shí)現(xiàn)接口的問(wèn)題了。 也就是說(shuō),可以用CGLib來(lái)生成上文中的PeelFruitProxy。

簡(jiǎn)單介紹一下怎么用,首先這個(gè)CGLib是一個(gè)三方的庫(kù),我們要把它依賴(lài)進(jìn)來(lái):

compile 'cglib:cglib:3.2.8'

最新版本可以在這里看(新版本)[https://github.com/cglib/cglib/releases] 然后我們來(lái)試一試,我們來(lái)實(shí)現(xiàn)一下上面的代理

//FruitMethodInterceptor.java/**
 * CGLib代理的方法攔截器
 */public class FruitMethodInterceptor implements MethodInterceptor{    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        String result = (String) proxy.invokeSuper(obj, args);//主要,這里調(diào)用的是父類(lèi),也就是說(shuō), 生成的類(lèi)和原始類(lèi)是繼承關(guān)系
        return "去皮的"+result;
    }
}//Main.javapublic class Main {    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Apple.class);
        enhancer.setCallback(new FruitMethodInterceptor());
        Apple apple = (Apple) enhancer.create();
        System.out.println(apple.getClass().getName());
        System.out.println(apple.getName());
    }
}

運(yùn)行效果如下:

java中的代理是指什么

我們看到,實(shí)現(xiàn)了同樣的功能,但是,Apple已經(jīng)不是原來(lái)的Apple類(lèi)了,變成了com.zjiecode.learn.java.proxy.Apple$$EnhancerByCGLIB$$44ade224,沒(méi)錯(cuò),我們正真使用的是這個(gè)類(lèi),而不是原來(lái)的Apple了,這個(gè)類(lèi)繼承自Apple,最后實(shí)現(xiàn)了對(duì)Apple類(lèi)的代理。 這種方式,因?yàn)槭褂玫氖抢^承,所以,無(wú)需被代理的類(lèi)實(shí)現(xiàn)接口。當(dāng)然,他也可以通過(guò)接口來(lái)實(shí)現(xiàn)代理。

總結(jié)

第一種代理,就不說(shuō)了,只適合單一的一個(gè)接口的代理,在編譯時(shí)就決定好了。

第二、三種代理,都是動(dòng)態(tài)時(shí)代理 ,但是我們看到也有差別:

1)JDK的動(dòng)態(tài)代理 ,只能實(shí)現(xiàn)接口代理,并且是包裝的被代理對(duì)象(類(lèi)的實(shí)例),也就是說(shuō),在代理的過(guò)程中,有2個(gè)對(duì)象,一個(gè)代理對(duì)象,一個(gè)目標(biāo)對(duì)象,目標(biāo)對(duì)象被包裝在代理對(duì)象里面。

2)CGLib的代理,是繼承目標(biāo)對(duì)象,生成了一個(gè)新的類(lèi),然后來(lái)實(shí)現(xiàn)代理,這樣,在內(nèi)存中就是有代理對(duì)象,沒(méi)有目標(biāo)對(duì)象了,使用的是直接繼承的方式

生成代理類(lèi)是在運(yùn)行時(shí),有別于javapoet在編譯時(shí)生成類(lèi)。

以上就是java中的代理,小編相信有部分知識(shí)點(diǎn)可能是我們?nèi)粘9ぷ鲿?huì)見(jiàn)到或用到的。希望你能通過(guò)這篇文章學(xué)到更多知識(shí)。更多詳情敬請(qǐng)關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI