您好,登錄后才能下訂單哦!
這篇文章主要介紹“什么是靜態代理與動態代理”,在日常操作中,相信很多人在什么是靜態代理與動態代理問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”什么是靜態代理與動態代理”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
開場
一位穿著藍色襯衫,牛仔褲,拿著一個白色保溫杯的中年男子急匆匆地坐在你對面,看樣子是項目上的東西很急,估摸面試時間不會太長,這樣一想心情放松了許多......(后來我就被打臉了)
面試開始
面試官:小伙子,我看你的簡歷上說精通java基礎對吧,那我先簡單來問幾個java基礎。
好的好的,面試官你問。(一聽到簡單兩個字就內心竊喜......)
面試官:你知道Java中有個東西叫代理嗎?
知道知道,代理就是通過代理對象去訪問實際的目標對象,比如我們在生活中租房,可以直接找房東,也可以通過某些租房平臺去租房,通過租房平臺的這種方式就是代理。在java中這種租房平臺就被叫做代理類,代理類不僅能實現目標對象,還能增加一些額外的功能。據我所知java中的代理方式有靜態代理和動態代理。(這個時候面試官很大概率會問你這兩種代理模式)。
面試官:沒想到你還能通過生活中的現象去理解代碼,不錯不錯,我看你提到了靜態代理和動態代理,那你給我說說什么是靜態代理吧
(果然問了,還好我做了準備)靜態代理就是在代碼運行之前,這個代理類就已經存在了。還是以上面的租房為例,在代碼中會首先創建一個通用的租房接口:
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("租房后收取服務費"); } }
代理類可以在不改變被代理對象的情況下增加功能,最后我們測試一下這個靜態代理:
public class Main { public static void main(String[] args) { RealRoom realRoom =new RealRoom("碧桂園"); ProxyClass proxyClass=new ProxyClass(realRoom); proxyClass.rent(); } }
然后觀察結果:
租房前收取中介費 租了碧桂園 租房后收取服務費
面試官:既然靜態代理那么強大,那他有什么缺點嗎?
由于靜態代理在代碼運行之前就已經存在代理類,因此對于每一個代理對象都需要建一個代理類去代理,當需要代理的對象很多時就需要創建很多的代理類,嚴重降低程序的可維護性。用動態代理就可以解決這個問題。
面試官:那你給我講一講動態代理吧
動態代理是指代理類不是寫在代碼中,而是在運行過程中產生的,java提供了兩種實現動態代理的方式,分別是基于Jdk的動態代理和基于Cglib的動態代理。
面試官:基于JDK的動態代理我忘了,你給我復習復習。
(我???算了算了) 實現Jdk的動態代理需要實現InvocationHandler接口,然后實現其中的invoke方法。如果代理的方法被調用,那么代理便會通知和轉發給內部的 InvocationHandler 實現類invoke,由它實現處理內容。
public class ProxyHandler implements InvocationHandler { Object object; public ProxyHandler(Object object) { this.object = object; } //proxy 代理對象 //method 要實現的方法 //args 方法的參數 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("代理執行之前:"+method.getName()); Object invoke = method.invoke(object, args); System.out.println("代理執行之后:"+method.getName()); return invoke; } }
接下來在main方法中執行動態代理
public static void main(String[] args) { Room room=new RealRoom("碧桂園"); //obj.getClass().getClassLoader()類加載器 //obj.getClass().getInterfaces() 目標類實現的接口 //InvocationHandler對象 InvocationHandler invocationHandler=new ProxyHandler(room); Room proxyRoom = (Room) Proxy.newProxyInstance(room.getClass().getClassLoader(), room.getClass().getInterfaces(), invocationHandler); proxyRoom.rent(); }
這段代碼的核心是Proxy.newProxyInstance,目的是運行期間生成代理類,最后通過代理類執行被代理的方法。最后結果如下:
代理執行之前:rent 租了碧桂園 代理執行之后:rent
面試官:被你這么一說我想起來動態代理了,那他的優勢呢?
之前我講靜態代理的時候說靜態代理的缺點在于對于每一個被代理的對象,都需要建一個代理類。因為靜態代理是在項目運行前就寫好的。但是動態代理就不是這樣,由于動態代理在運行時才創建代理類,因此只需要寫一個動態代理類就好。比如我再創建一個被代理的對象賣房:
寫一個通用接口Sell
public interface Sell { void sellRoom(); }
接著還是寫一個被代理對象的類:
public class RealSell implements Sell { public void sellRoom() { System.out.println("賣房了"); } }
接下來在main方法中執行動態代理
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(); }
最終實現結果如下:
代理執行之前:sellRoom 賣房了 代理執行之后:sellRoom
通過動態代理,我可以通過一個動態代理類,去代理多個對象。
面試官:如果我記的沒錯,通過這種方式只能代理接口吧,我看你上面的例子也都是代理接口,那我如果想代理類該怎么辦呢?
jdk動態代理確實只能代理接口,JDK動態代理是基于接口的方式,換句話來說就是代理類和目標類都實現同一個接口。如果想要代理類的話可以使用CGLib,CGLib動態代理是代理類去繼承目標類,然后實現目標類的方法。
創建一個目標類CGRoom
public class CGRoom { public void rent(String roomName){ System.out.println("租了"+roomName); } }
創建cglib的動態代理類,繼承MethodInterceptor ,實現其中的intercept方法
public class MyMethodInterceptor implements MethodInterceptor { public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("代理執行之前:"+method.getName()); Object object=methodProxy.invokeSuper(o,objects); System.out.println("代理執行之后:"+method.getName()); return object; } }
最后通過enhance對象來創建代理類
public static void main(String[] args) { //創建Enhancer對象,類似于JDK動態代理的Proxy類,下一步就是設置幾個參數 Enhancer enhancer=new Enhancer(); //設置目標類的字節碼文件 enhancer.setSuperclass(CGRoom.class); //設置回調函數 enhancer.setCallback(new MyMethodInterceptor()); //創建代理對象 CGRoom proxy= (CGRoom) enhancer.create(); proxy.rent("碧桂園"); }
最終實現以下結果:
代理執行之前:rent 租了碧桂園 代理執行之后:rent
面試官:既然動態代理被你說的這么牛,那你平常工作中有使用到嗎?
平常我的業務代碼中雖然幾乎沒有使用過動態代理,但是我工作中使用的Spring系列框架中的AOP,以及RPC框架中都用到了動態代理,以AOP為例,AOP通過動態代理對目標對象進行了增強,比如我們最常用的前置通知、后置通知等。
面試官:不錯!下面再考你幾個基礎,說說你對注解的理解,注解又解決了哪些問題?
Java語言中的類、方法、變量、參數和包都可以用注解標記,程序運行過程中我們可以獲取到相應的注解以及注解中定義的內容,比如說 Spring 中如果檢測到說你的類被 @Component注解標記的話,Spring 容器在啟動的時候就會把這個類歸為自己管理,這樣你就可以通過 @Autowired注解注入這個對象了。
面試官:那你知道如何自己去定義注解嗎?
知道知道,自定義注解主要有以下四步:
第一步通過@interface聲明注解:
public @interface Myannotation { String key() default ""; }
第二步通過四種元注解修飾注解:(面試的時候說出這四種注解就可以了)
元注解的作用就是負責其他注解,java中一共有四個元注解,分別是@Target,@Retention,@Documented,@Inherited,下面先介紹以下四種注解的作用:
@Target:Target說明了注解所修飾的對象范圍,取值(ElementType)有:
用于描述構造器
用于描述屬性
用于描述局部變量
用于描述方法
用于描述包
用于描述參數
用于描述類、接口(包括注解類型)或者enum聲明
@Retention:Retention定義了注解的保留范圍,取值(RetentionPoicy)有:
在源文件中有效(即源文件保留)
在class文件中有效(即class保留)
在運行時有效(即運行時保留)
@Documented:Documented用于描述其它類型的annotation應該被作為被標注的程序成員的公共API,因此可以被例如javadoc此類的工具文檔化。Documented是一個標記注解,沒有成員。
@Inherited:Inherited 元注解是一個標記注解,@Inherited闡述了某個被標注的類型是被繼承的。如果一個使用了@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()); } } }
輸出結果:
配置了自定義注解 屬性:username上的注解key為javayz
面試官:我看你上面第四步提到了反射是吧?那你給我講講什么是反射,它有啥特點:
(我暈,我就說了反射兩個字啊,還好有準備)JAVA 反射機制是在運行狀態中,對于任意一個類,都能夠知道這個類的所有屬性和方法;對于任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱為 java 語言的反射機制。
在上面第四步利用反射解析注解中,我通過MyannotationTest.class獲取到了MyannotationTest的類對象,又用myclass.getDeclaredFields();獲取到了所有的屬性。這就是反射。
到此,關于“什么是靜態代理與動態代理”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。