溫馨提示×

溫馨提示×

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

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

什么是靜態(tài)代理與動態(tài)代理

發(fā)布時間:2021-10-21 09:46:47 來源:億速云 閱讀:113 作者:iii 欄目:編程語言

這篇文章主要介紹“什么是靜態(tài)代理與動態(tài)代理”,在日常操作中,相信很多人在什么是靜態(tài)代理與動態(tài)代理問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”什么是靜態(tài)代理與動態(tài)代理”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!

開場

一位穿著藍(lán)色襯衫,牛仔褲,拿著一個白色保溫杯的中年男子急匆匆地坐在你對面,看樣子是項目上的東西很急,估摸面試時間不會太長,這樣一想心情放松了許多......(后來我就被打臉了)

什么是靜態(tài)代理與動態(tài)代理

面試開始

面試官:小伙子,我看你的簡歷上說精通java基礎(chǔ)對吧,那我先簡單來問幾個java基礎(chǔ)。

好的好的,面試官你問。(一聽到簡單兩個字就內(nèi)心竊喜......)

面試官:你知道Java中有個東西叫代理嗎?

知道知道,代理就是通過代理對象去訪問實際的目標(biāo)對象,比如我們在生活中租房,可以直接找房東,也可以通過某些租房平臺去租房,通過租房平臺的這種方式就是代理。在java中這種租房平臺就被叫做代理類,代理類不僅能實現(xiàn)目標(biāo)對象,還能增加一些額外的功能。據(jù)我所知java中的代理方式有靜態(tài)代理和動態(tài)代理。(這個時候面試官很大概率會問你這兩種代理模式)。

面試官:沒想到你還能通過生活中的現(xiàn)象去理解代碼,不錯不錯,我看你提到了靜態(tài)代理和動態(tài)代理,那你給我說說什么是靜態(tài)代理吧

(果然問了,還好我做了準(zhǔn)備)靜態(tài)代理就是在代碼運行之前,這個代理類就已經(jīng)存在了。還是以上面的租房為例,在代碼中會首先創(chuàng)建一個通用的租房接口:

public interface Room {
    void rent();
}

然后需要有一個被代理的類(或者稱為真實的類)和一個代理類:

public class RealRoom implements Room {
    private String roomname;
    public RealRoom(String roomname) {
        this.roomname = roomname;
    }
    public void rent() {
        System.out.println("租了"+roomname);
    }
}

代理類如下:

public class ProxyClass implements Room {
    RealRoom realRoom;
    public ProxyClass(RealRoom realRoom) {
        this.realRoom = realRoom;
    }
    public void rent() {
        System.out.println("租房前收取中介費");
        realRoom.rent();
        System.out.println("租房后收取服務(wù)費");
    }
}

代理類可以在不改變被代理對象的情況下增加功能,最后我們測試一下這個靜態(tài)代理:

public class Main {
    public static void main(String[] args) {
        RealRoom realRoom =new RealRoom("碧桂園");
        ProxyClass proxyClass=new ProxyClass(realRoom);
        proxyClass.rent();
    }
}

然后觀察結(jié)果:

租房前收取中介費
租了碧桂園
租房后收取服務(wù)費

面試官:既然靜態(tài)代理那么強大,那他有什么缺點嗎?

由于靜態(tài)代理在代碼運行之前就已經(jīng)存在代理類,因此對于每一個代理對象都需要建一個代理類去代理,當(dāng)需要代理的對象很多時就需要創(chuàng)建很多的代理類,嚴(yán)重降低程序的可維護(hù)性。用動態(tài)代理就可以解決這個問題。

面試官:那你給我講一講動態(tài)代理吧

動態(tài)代理是指代理類不是寫在代碼中,而是在運行過程中產(chǎn)生的,java提供了兩種實現(xiàn)動態(tài)代理的方式,分別是基于Jdk的動態(tài)代理和基于Cglib的動態(tài)代理。

面試官:基于JDK的動態(tài)代理我忘了,你給我復(fù)習(xí)復(fù)習(xí)。

(我???算了算了) 實現(xiàn)Jdk的動態(tài)代理需要實現(xiàn)InvocationHandler接口,然后實現(xiàn)其中的invoke方法。如果代理的方法被調(diào)用,那么代理便會通知和轉(zhuǎn)發(fā)給內(nèi)部的 InvocationHandler 實現(xiàn)類invoke,由它實現(xiàn)處理內(nèi)容。

public class ProxyHandler implements InvocationHandler {
    Object object;
    public ProxyHandler(Object object) {
        this.object = object;
    }
    //proxy 代理對象
    //method 要實現(xiàn)的方法
    //args 方法的參數(shù)    
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理執(zhí)行之前:"+method.getName());
        Object invoke = method.invoke(object, args);
        System.out.println("代理執(zhí)行之后:"+method.getName());
        return invoke;
    }
}

接下來在main方法中執(zhí)行動態(tài)代理

public static void main(String[] args) {
    Room room=new RealRoom("碧桂園");
    //obj.getClass().getClassLoader()類加載器
    //obj.getClass().getInterfaces() 目標(biāo)類實現(xiàn)的接口
    //InvocationHandler對象
    InvocationHandler invocationHandler=new ProxyHandler(room);
    Room proxyRoom = (Room) Proxy.newProxyInstance(room.getClass().getClassLoader(), room.getClass().getInterfaces(), invocationHandler);
    proxyRoom.rent();
}

這段代碼的核心是Proxy.newProxyInstance,目的是運行期間生成代理類,最后通過代理類執(zhí)行被代理的方法。最后結(jié)果如下:

代理執(zhí)行之前:rent
租了碧桂園
代理執(zhí)行之后:rent

面試官:被你這么一說我想起來動態(tài)代理了,那他的優(yōu)勢呢?

之前我講靜態(tài)代理的時候說靜態(tài)代理的缺點在于對于每一個被代理的對象,都需要建一個代理類。因為靜態(tài)代理是在項目運行前就寫好的。但是動態(tài)代理就不是這樣,由于動態(tài)代理在運行時才創(chuàng)建代理類,因此只需要寫一個動態(tài)代理類就好。比如我再創(chuàng)建一個被代理的對象賣房:

寫一個通用接口Sell

public interface Sell {
    void sellRoom();
}

接著還是寫一個被代理對象的類:

public class RealSell implements Sell {
    public void sellRoom() {
        System.out.println("賣房了");
    }
}

接下來在main方法中執(zhí)行動態(tài)代理

    public static void main(String[] args) {
        Sell sell=new RealSell();
        InvocationHandler invocationHandler=new ProxyHandler(sell);
        Sell proxysell= (Sell) Proxy.newProxyInstance(sell.getClass().getClassLoader(),sell.getClass().getInterfaces(),invocationHandler);
        proxysell.sellRoom();
    }

最終實現(xiàn)結(jié)果如下:

代理執(zhí)行之前:sellRoom
賣房了
代理執(zhí)行之后:sellRoom

通過動態(tài)代理,我可以通過一個動態(tài)代理類,去代理多個對象。

面試官:如果我記的沒錯,通過這種方式只能代理接口吧,我看你上面的例子也都是代理接口,那我如果想代理類該怎么辦呢?

jdk動態(tài)代理確實只能代理接口,JDK動態(tài)代理是基于接口的方式,換句話來說就是代理類和目標(biāo)類都實現(xiàn)同一個接口。如果想要代理類的話可以使用CGLib,CGLib動態(tài)代理是代理類去繼承目標(biāo)類,然后實現(xiàn)目標(biāo)類的方法。

創(chuàng)建一個目標(biāo)類CGRoom

public class CGRoom {
    public void rent(String roomName){
        System.out.println("租了"+roomName);
    }
}

創(chuàng)建cglib的動態(tài)代理類,繼承MethodInterceptor ,實現(xiàn)其中的intercept方法

public class MyMethodInterceptor implements MethodInterceptor {

    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("代理執(zhí)行之前:"+method.getName());
        Object object=methodProxy.invokeSuper(o,objects);
        System.out.println("代理執(zhí)行之后:"+method.getName());
        return object;
    }
}

最后通過enhance對象來創(chuàng)建代理類

public static void main(String[] args) {
    //創(chuàng)建Enhancer對象,類似于JDK動態(tài)代理的Proxy類,下一步就是設(shè)置幾個參數(shù)
    Enhancer enhancer=new Enhancer();
    //設(shè)置目標(biāo)類的字節(jié)碼文件
    enhancer.setSuperclass(CGRoom.class);
    //設(shè)置回調(diào)函數(shù)
    enhancer.setCallback(new MyMethodInterceptor());
    //創(chuàng)建代理對象
    CGRoom proxy= (CGRoom) enhancer.create();
    proxy.rent("碧桂園");
}

最終實現(xiàn)以下結(jié)果:

代理執(zhí)行之前:rent
租了碧桂園
代理執(zhí)行之后:rent

面試官:既然動態(tài)代理被你說的這么牛,那你平常工作中有使用到嗎?

平常我的業(yè)務(wù)代碼中雖然幾乎沒有使用過動態(tài)代理,但是我工作中使用的Spring系列框架中的AOP,以及RPC框架中都用到了動態(tài)代理,以AOP為例,AOP通過動態(tài)代理對目標(biāo)對象進(jìn)行了增強,比如我們最常用的前置通知、后置通知等。

面試官:不錯!下面再考你幾個基礎(chǔ),說說你對注解的理解,注解又解決了哪些問題?

Java語言中的類、方法、變量、參數(shù)和包都可以用注解標(biāo)記,程序運行過程中我們可以獲取到相應(yīng)的注解以及注解中定義的內(nèi)容,比如說 Spring 中如果檢測到說你的類被 @Component注解標(biāo)記的話,Spring 容器在啟動的時候就會把這個類歸為自己管理,這樣你就可以通過 @Autowired注解注入這個對象了。

面試官:那你知道如何自己去定義注解嗎?

知道知道,自定義注解主要有以下四步:

第一步通過@interface聲明注解:

public @interface Myannotation {
    String key() default "";
}

第二步通過四種元注解修飾注解:(面試的時候說出這四種注解就可以了)

元注解的作用就是負(fù)責(zé)其他注解,java中一共有四個元注解,分別是@Target,@Retention,@Documented,@Inherited,下面先介紹以下四種注解的作用:

@Target:Target說明了注解所修飾的對象范圍,取值(ElementType)有:

  1. 用于描述構(gòu)造器

  1. 用于描述屬性

  2. 用于描述局部變量

  1. 用于描述方法

  2. 用于描述包

  3.  用于描述參數(shù)

  4.  用于描述類、接口(包括注解類型)或者enum聲明

@Retention:Retention定義了注解的保留范圍,取值(RetentionPoicy)有:

  1. 在源文件中有效(即源文件保留)

  1. 在class文件中有效(即class保留)

  2. 在運行時有效(即運行時保留)

@Documented:Documented用于描述其它類型的annotation應(yīng)該被作為被標(biāo)注的程序成員的公共API,因此可以被例如javadoc此類的工具文檔化。Documented是一個標(biāo)記注解,沒有成員。

@Inherited:Inherited 元注解是一個標(biāo)記注解,@Inherited闡述了某個被標(biāo)注的類型是被繼承的。如果一個使用了@Inherited修飾的annotation類型被用于一個class,則這個annotation將被用于該class的子類。 

@Target({ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface Myannotation {
    String key() default "";
}

第三步使用注解,因為定義Target時定義了MEHTOD和FIELD,因此可以在屬性和方法中使用這個注解:

public class MyannotationTest {
    @Myannotation(key = "javayz")
    private String username;
}

第四步利用反射解析注解

public static void main(String[] args) {
    Class myclass=MyannotationTest.class;
    Field[] fields = myclass.getDeclaredFields();
    for (Field field :fields){
        if (field.isAnnotationPresent(Myannotation.class)){
            System.out.println("配置了自定義注解");
            Myannotation annotation = field.getAnnotation(Myannotation.class);
            System.out.println("屬性:"+field.getName()+"上的注解key為"+annotation.key());
        }
    }
}

輸出結(jié)果:

配置了自定義注解
屬性:username上的注解key為javayz

面試官:我看你上面第四步提到了反射是吧?那你給我講講什么是反射,它有啥特點:

(我暈,我就說了反射兩個字啊,還好有準(zhǔn)備)JAVA 反射機制是在運行狀態(tài)中,對于任意一個類,都能夠知道這個類的所有屬性和方法;對于任意一個對象,都能夠調(diào)用它的任意一個方法和屬性;這種動態(tài)獲取的信息以及動態(tài)調(diào)用對象的方法的功能稱為 java 語言的反射機制。

在上面第四步利用反射解析注解中,我通過MyannotationTest.class獲取到了MyannotationTest的類對象,又用myclass.getDeclaredFields();獲取到了所有的屬性。這就是反射。

到此,關(guān)于“什么是靜態(tài)代理與動態(tài)代理”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>

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

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

AI