成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

動(dòng)態(tài)代理總結(jié),面試你要知道的都在這里,無(wú)廢話!

開(kāi)發(fā) 前端
代理模式是一種設(shè)計(jì)模式,提供了對(duì)目標(biāo)對(duì)象額外的訪問(wèn)方式,即通過(guò)代理對(duì)象訪問(wèn)目標(biāo)對(duì)象,這樣可以在不修改原目標(biāo)對(duì)象的前提下,提供額外的功能操作,擴(kuò)展目標(biāo)對(duì)象的功能。

[[399678]]

本文轉(zhuǎn)載自微信公眾號(hào)「月伴飛魚(yú)」,作者日常加油站。轉(zhuǎn)載本文請(qǐng)聯(lián)系月伴飛魚(yú)公眾號(hào)。

代理模式

代理模式是一種設(shè)計(jì)模式,提供了對(duì)目標(biāo)對(duì)象額外的訪問(wèn)方式,即通過(guò)代理對(duì)象訪問(wèn)目標(biāo)對(duì)象,這樣可以在不修改原目標(biāo)對(duì)象的前提下,提供額外的功能操作,擴(kuò)展目標(biāo)對(duì)象的功能

一個(gè)比方:在租房的時(shí)候,有的人會(huì)通過(guò)房東直租,有的人會(huì)通過(guò)中介租房。

這兩種情況哪種比較方便呢?當(dāng)然是通過(guò)中介更加方便。

這里的中介就相當(dāng)于代理,用戶通過(guò)中介完成租房的一系列操作(看房、交押金、租房、清掃衛(wèi)生)代理模式可以有效的將具體的實(shí)現(xiàn)與調(diào)用方進(jìn)行解耦,通過(guò)面向接口進(jìn)行編碼完全將具體的實(shí)現(xiàn)隱藏在內(nèi)部。

分類:

靜態(tài)代理: 在編譯時(shí)就已經(jīng)實(shí)現(xiàn),編譯完成后代理類是一個(gè)實(shí)際的class文件

動(dòng)態(tài)代理: 在運(yùn)行時(shí)動(dòng)態(tài)生成的,即編譯完成后沒(méi)有實(shí)際的class文件,而是在運(yùn)行時(shí)動(dòng)態(tài)生成類字節(jié)碼,并加載到JVM中

靜態(tài)代理

使用方式

創(chuàng)建一個(gè)接口,然后創(chuàng)建被代理的類實(shí)現(xiàn)該接口并且實(shí)現(xiàn)該接口中的抽象方法。之后再創(chuàng)建一個(gè)代理類,同時(shí)使其也實(shí)現(xiàn)這個(gè)接口。在代理類中持有一個(gè)被代理對(duì)象的引用,而后在代理類方法中調(diào)用該對(duì)象的方法。

  1. public interface UserDao {     
  2.   void save();      
  1. public class UserDaoImpl implements UserDao { 
  2.     @Override 
  3.     public void save() { 
  4.         System.out.println("正在保存用戶..."); 
  5.     } 
  1. public class TransactionHandler implements UserDao { 
  2.     //目標(biāo)代理對(duì)象 
  3.     private UserDao target; 
  4.     //構(gòu)造代理對(duì)象時(shí)傳入目標(biāo)對(duì)象 
  5.     public TransactionHandler(UserDao target) { 
  6.         this.target = target; 
  7.     } 
  8.     @Override 
  9.     public void save() { 
  10.         //調(diào)用目標(biāo)方法前的處理 
  11.         System.out.println("開(kāi)啟事務(wù)控制..."); 
  12.         //調(diào)用目標(biāo)對(duì)象的方法 
  13.         target.save(); 
  14.         //調(diào)用目標(biāo)方法后的處理 
  15.         System.out.println("關(guān)閉事務(wù)控制..."); 
  16.     } 
  1. public class Main { 
  2.     public static void main(String[] args) { 
  3.         //新建目標(biāo)對(duì)象 
  4.         UserDaoImpl target = new UserDaoImpl(); 
  5.         //創(chuàng)建代理對(duì)象, 并使用接口對(duì)其進(jìn)行引用 
  6.         UserDao userDao = new TransactionHandler(target); 
  7.         //針對(duì)接口進(jìn)行調(diào)用 
  8.         userDao.save(); 
  9.     } 

使用JDK靜態(tài)代理很容易就完成了對(duì)一個(gè)類的代理操作。但是JDK靜態(tài)代理的缺點(diǎn)也暴露了出來(lái):由于代理只能為一個(gè)類服務(wù),如果需要代理的類很多,那么就需要編寫(xiě)大量的代理類,比較繁瑣

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

使用JDK動(dòng)態(tài)代理的五大步驟:

  1. 通過(guò)實(shí)現(xiàn)InvocationHandler接口來(lái)自定義自己的InvocationHandler;
  2. 通過(guò)Proxy.getProxyClass獲得動(dòng)態(tài)代理類;
  3. 通過(guò)反射機(jī)制獲得代理類的構(gòu)造方法,方法簽名為getConstructor(InvocationHandler.class);
  4. 通過(guò)構(gòu)造函數(shù)獲得代理對(duì)象并將自定義的InvocationHandler實(shí)例對(duì)象傳為參數(shù)傳入;
  5. 通過(guò)代理對(duì)象調(diào)用目標(biāo)方法;
  1. public interface IHello { 
  2.     void sayHello(); 
  1. public class HelloImpl implements IHello { 
  2.    @Override 
  3.    public void sayHello() { 
  4.        System.out.println("Hello world!"); 
  5.    } 
  1. import java.lang.reflect.InvocationHandler; 
  2. import java.lang.reflect.Method; 
  3.   
  4. public class MyInvocationHandler implements InvocationHandler { 
  5.   
  6.     /** 目標(biāo)對(duì)象 */ 
  7.     private Object target; 
  8.   
  9.     public MyInvocationHandler(Object target){ 
  10.         this.target = target; 
  11.     } 
  12.   
  13.     @Override 
  14.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
  15.         System.out.println("------插入前置通知代碼-------------"); 
  16.         // 執(zhí)行相應(yīng)的目標(biāo)方法 
  17.         Object rs = method.invoke(target,args); 
  18.         System.out.println("------插入后置處理代碼-------------"); 
  19.         return rs; 
  20.     } 
  1. import java.lang.reflect.Constructor; 
  2. import java.lang.reflect.InvocationHandler; 
  3. import java.lang.reflect.InvocationTargetException; 
  4. import java.lang.reflect.Proxy; 
  5.  
  6. public class MyProxyTest { 
  7.     public static void main(String[] args) 
  8.             throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException { 
  9.         // =========================第一種========================== 
  10.         // 1、生成$Proxy0的class文件 
  11.         System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles""true"); 
  12.         // 2、獲取動(dòng)態(tài)代理類 
  13.         Class proxyClazz = Proxy.getProxyClass(IHello.class.getClassLoader(),IHello.class); 
  14.         // 3、獲得代理類的構(gòu)造函數(shù),并傳入?yún)?shù)類型InvocationHandler.class 
  15.         Constructor constructor = proxyClazz.getConstructor(InvocationHandler.class); 
  16.         // 4、通過(guò)構(gòu)造函數(shù)來(lái)創(chuàng)建動(dòng)態(tài)代理對(duì)象,將自定義的InvocationHandler實(shí)例傳入 
  17.         IHello iHello1 = (IHello) constructor.newInstance(new MyInvocationHandler(new HelloImpl())); 
  18.         // 5、通過(guò)代理對(duì)象調(diào)用目標(biāo)方法 
  19.         iHello1.sayHello(); 
  20.   
  21.         // ==========================第二種============================= 
  22.         /** 
  23.          * Proxy類中還有個(gè)將2~4步驟封裝好的簡(jiǎn)便方法來(lái)創(chuàng)建動(dòng)態(tài)代理對(duì)象, 
  24.          *其方法簽名為:newProxyInstance(ClassLoader loader,Class<?>[] instance, InvocationHandler h) 
  25.          */ 
  26.         IHello  iHello2 = (IHello) Proxy.newProxyInstance(IHello.class.getClassLoader(), // 加載接口的類加載器 
  27.                 new Class[]{IHello.class}, // 一組接口 
  28.                 new MyInvocationHandler(new HelloImpl())); // 自定義的InvocationHandler 
  29.         iHello2.sayHello(); 
  30.     } 

JDK靜態(tài)代理與JDK動(dòng)態(tài)代理之間有些許相似,比如說(shuō)都要?jiǎng)?chuàng)建代理類,以及代理類都要實(shí)現(xiàn)接口等。

不同之處: 在靜態(tài)代理中我們需要對(duì)哪個(gè)接口和哪個(gè)被代理類創(chuàng)建代理類,所以我們?cè)诰幾g前就需要代理類實(shí)現(xiàn)與被代理類相同的接口,并且直接在實(shí)現(xiàn)的方法中調(diào)用被代理類相應(yīng)的方法;但是動(dòng)態(tài)代理則不同,我們不知道要針對(duì)哪個(gè)接口、哪個(gè)被代理類創(chuàng)建代理類,因?yàn)樗窃谶\(yùn)行時(shí)被創(chuàng)建的。

一句話來(lái)總結(jié)一下JDK靜態(tài)代理和JDK動(dòng)態(tài)代理的區(qū)別:

JDK靜態(tài)代理是通過(guò)直接編碼創(chuàng)建的,而JDK動(dòng)態(tài)代理是利用反射機(jī)制在運(yùn)行時(shí)創(chuàng)建代理類的。

其實(shí)在動(dòng)態(tài)代理中,核心是InvocationHandler。每一個(gè)代理的實(shí)例都會(huì)有一個(gè)關(guān)聯(lián)的調(diào)用處理程序(InvocationHandler)。對(duì)待代理實(shí)例進(jìn)行調(diào)用時(shí),將對(duì)方法的調(diào)用進(jìn)行編碼并指派到它的調(diào)用處理器(InvocationHandler)的invoke方法

對(duì)代理對(duì)象實(shí)例方法的調(diào)用都是通過(guò)InvocationHandler中的invoke方法來(lái)完成的,而invoke方法會(huì)根據(jù)傳入的代理對(duì)象、方法名稱以及參數(shù)決定調(diào)用代理的哪個(gè)方法。

CGLIB

CGLIB包的底層是通過(guò)使用一個(gè)小而快的字節(jié)碼處理框架ASM,來(lái)轉(zhuǎn)換字節(jié)碼并生成新的類

CGLIB代理實(shí)現(xiàn)如下:

首先實(shí)現(xiàn)一個(gè)MethodInterceptor,方法調(diào)用會(huì)被轉(zhuǎn)發(fā)到該類的intercept()方法。

然后在需要使用的時(shí)候,通過(guò)CGLIB動(dòng)態(tài)代理獲取代理對(duì)象。

使用案例

  1.  public class HelloService { 
  2.   
  3.     public HelloService() { 
  4.         System.out.println("HelloService構(gòu)造"); 
  5.     } 
  6.   
  7.     /** 
  8.      * 該方法不能被子類覆蓋,Cglib是無(wú)法代理final修飾的方法的 
  9.      */ 
  10.     final public String sayOthers(String name) { 
  11.         System.out.println("HelloService:sayOthers>>"+name); 
  12.         return null
  13.     } 
  14.   
  15.     public void sayHello() { 
  16.         System.out.println("HelloService:sayHello"); 
  17.     } 
  1. import net.sf.cglib.proxy.MethodInterceptor; 
  2. import net.sf.cglib.proxy.MethodProxy; 
  3.   
  4. import java.lang.reflect.Method; 
  5.   
  6. /** 
  7.  * 自定義MethodInterceptor 
  8.  */ 
  9. public class MyMethodInterceptor implements MethodInterceptor{ 
  10.   
  11.     /** 
  12.      * sub:cglib生成的代理對(duì)象 
  13.      * method:被代理對(duì)象方法 
  14.      * objects:方法入?yún)?nbsp;
  15.      * methodProxy: 代理方法 
  16.      */ 
  17.     @Override 
  18.     public Object intercept(Object sub, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { 
  19.         System.out.println("======插入前置通知======"); 
  20.         Object object = methodProxy.invokeSuper(sub, objects); 
  21.         System.out.println("======插入后者通知======"); 
  22.         return object; 
  23.     } 
  1. import net.sf.cglib.core.DebuggingClassWriter; 
  2. import net.sf.cglib.proxy.Enhancer; 
  3.   
  4. public class Client { 
  5.     public static void main(String[] args) { 
  6.         // 代理類class文件存入本地磁盤(pán)方便我們反編譯查看源碼 
  7.         System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code"); 
  8.         // 通過(guò)CGLIB動(dòng)態(tài)代理獲取代理對(duì)象的過(guò)程 
  9.         Enhancer enhancer = new Enhancer(); 
  10.         // 設(shè)置enhancer對(duì)象的父類 
  11.         enhancer.setSuperclass(HelloService.class); 
  12.         // 設(shè)置enhancer的回調(diào)對(duì)象 
  13.         enhancer.setCallback(new MyMethodInterceptor()); 
  14.         // 創(chuàng)建代理對(duì)象 
  15.         HelloService proxy= (HelloService)enhancer.create(); 
  16.         // 通過(guò)代理對(duì)象調(diào)用目標(biāo)方法 
  17.         proxy.sayHello(); 
  18.     } 

JDK代理要求被代理的類必須實(shí)現(xiàn)接口,有很強(qiáng)的局限性。

而CGLIB動(dòng)態(tài)代理則沒(méi)有此類強(qiáng)制性要求。簡(jiǎn)單的說(shuō),CGLIB會(huì)讓生成的代理類繼承被代理類,并在代理類中對(duì)代理方法進(jìn)行強(qiáng)化處理(前置處理、后置處理等)。

總結(jié)一下CGLIB在進(jìn)行代理的時(shí)候都進(jìn)行了哪些工作

  • 生成的代理類繼承被代理類。在這里我們需要注意一點(diǎn):如果委托類被final修飾,那么它不可被繼承,即不可被代理;同樣,如果委托類中存在final修飾的方法,那么該方法也不可被代理
  • 代理類會(huì)為委托方法生成兩個(gè)方法,一個(gè)是與委托方法簽名相同的方法,它在方法中會(huì)通過(guò)super調(diào)用委托方法;另一個(gè)是代理類獨(dú)有的方法
  • 當(dāng)執(zhí)行代理對(duì)象的方法時(shí),會(huì)首先判斷一下是否存在實(shí)現(xiàn)了MethodInterceptor接口的CGLIB$CALLBACK_0;,如果存在,則將調(diào)用MethodInterceptor中的intercept方法

在intercept方法中,我們除了會(huì)調(diào)用委托方法,還會(huì)進(jìn)行一些增強(qiáng)操作。在Spring AOP中,典型的應(yīng)用場(chǎng)景就是在某些敏感方法執(zhí)行前后進(jìn)行操作日志記錄

在CGLIB中,方法的調(diào)用并不是通過(guò)反射來(lái)完成的,而是直接對(duì)方法進(jìn)行調(diào)用:通過(guò)FastClass機(jī)制對(duì)Class對(duì)象進(jìn)行特別的處理,比如將會(huì)用數(shù)組保存method的引用,每次調(diào)用方法的時(shí)候都是通過(guò)一個(gè)index下標(biāo)來(lái)保持對(duì)方法的引用

Fastclass機(jī)制

CGLIB采用了FastClass的機(jī)制來(lái)實(shí)現(xiàn)對(duì)被攔截方法的調(diào)用。

FastClass機(jī)制就是對(duì)一個(gè)類的方法建立索引,通過(guò)索引來(lái)直接調(diào)用相應(yīng)的方法

  1. public class test10 { 
  2.   //這里,tt可以看作目標(biāo)對(duì)象,fc可以看作是代理對(duì)象;首先根據(jù)代理對(duì)象的getIndex方法獲取目標(biāo)方法的索引, 
  3.   //然后再調(diào)用代理對(duì)象的invoke方法就可以直接調(diào)用目標(biāo)類的方法,避免了反射 
  4.     public static void main(String[] args){ 
  5.         Test tt = new Test(); 
  6.         Test2 fc = new Test2(); 
  7.         int index = fc.getIndex("f()V"); 
  8.         fc.invoke(index, tt, null); 
  9.     } 
  10.  
  11. class Test{ 
  12.     public void f(){ 
  13.         System.out.println("f method"); 
  14.     } 
  15.      
  16.     public void g(){ 
  17.         System.out.println("g method"); 
  18.     } 
  19. class Test2{ 
  20.     public Object invoke(int index, Object o, Object[] ol){ 
  21.         Test t = (Test) o; 
  22.         switch(index){ 
  23.         case 1: 
  24.             t.f(); 
  25.             return null
  26.         case 2: 
  27.             t.g(); 
  28.             return null
  29.         } 
  30.         return null
  31.     } 
  32.     //這個(gè)方法對(duì)Test類中的方法建立索引 
  33.     public int getIndex(String signature){ 
  34.         switch(signature.hashCode()){ 
  35.         case 3078479: 
  36.             return 1; 
  37.         case 3108270: 
  38.             return 2; 
  39.         } 
  40.         return -1; 
  41.     } 

上例中,Test2是Test的Fastclass,在Test2中有兩個(gè)方法getIndex和invoke。

在getIndex方法中對(duì)Test的每個(gè)方法建立索引,并根據(jù)入?yún)?方法名+方法的描述符)來(lái)返回相應(yīng)的索引。

Invoke根據(jù)指定的索引,以ol為入?yún)⒄{(diào)用對(duì)象O的方法。這樣就避免了反射調(diào)用,提高了效率

三種代理方式之間對(duì)比

 

 

代理方式 實(shí)現(xiàn) 優(yōu)點(diǎn) 缺點(diǎn) 特點(diǎn)
JDK靜態(tài)代理 代理類與委托類實(shí)現(xiàn)同一接口,并且在代理類中需要硬編碼接口 實(shí)現(xiàn)簡(jiǎn)單,容易理解 代理類需要硬編碼接口,在實(shí)際應(yīng)用中可能會(huì)導(dǎo)致重復(fù)編碼,浪費(fèi)存儲(chǔ)空間并且效率很低 好像沒(méi)啥特點(diǎn)
JDK動(dòng)態(tài)代理 代理類與委托類實(shí)現(xiàn)同一接口,主要是通過(guò)代理類實(shí)現(xiàn)InvocationHandler并重寫(xiě)invoke方法來(lái)進(jìn)行動(dòng)態(tài)代理的,在invoke方法中將對(duì)方法進(jìn)行增強(qiáng)處理 不需要硬編碼接口,代碼復(fù)用率高 只能夠代理實(shí)現(xiàn)了接口的委托類 底層使用反射機(jī)制進(jìn)行方法的調(diào)用
CGLIB動(dòng)態(tài)代理 代理類將委托類作為自己的父類并為其中的非final委托方法創(chuàng)建兩個(gè)方法,一個(gè)是與委托方法簽名相同的方法,它在方法中會(huì)通過(guò)super調(diào)用委托方法;另一個(gè)是代理類獨(dú)有的方法。在代理方法中,它會(huì)判斷是否存在實(shí)現(xiàn)了MethodInterceptor接口的對(duì)象,若存在則將調(diào)用intercept方法對(duì)委托方法進(jìn)行代理 可以在運(yùn)行時(shí)對(duì)類或者是接口進(jìn)行增強(qiáng)操作,且委托類無(wú)需實(shí)現(xiàn)接口 不能對(duì)final類以及final方法進(jìn)行代理 底層將方法全部存入一個(gè)數(shù)組中,通過(guò)數(shù)組索引直接進(jìn)行方法調(diào)用

 

問(wèn)題

CGlib比JDK快?

  • 使用CGLiB實(shí)現(xiàn)動(dòng)態(tài)代理,CGLib底層采用ASM字節(jié)碼生成框架,使用字節(jié)碼技術(shù)生成代理類, 在jdk6之前比使用Java反射效率要高。唯一需要注意的是,CGLib不能對(duì)聲明為final的方法進(jìn)行代理, 因?yàn)镃GLib原理是動(dòng)態(tài)生成被代理類的子類。
  • 在jdk6、jdk7、jdk8逐步對(duì)JDK動(dòng)態(tài)代理優(yōu)化之后,在調(diào)用次數(shù)較少的情況下,JDK代理效率高于CGLIB代理效率。只有當(dāng)進(jìn)行大量調(diào)用的時(shí)候,jdk6和jdk7比CGLIB代理效率低一點(diǎn),但是到j(luò)dk8的時(shí)候,jdk代理效率高于CGLIB代理,總之,每一次jdk版本升級(jí),jdk代理效率都得到提升,而CGLIB代理消息確有點(diǎn)跟不上步伐。

Spring如何選擇用JDK還是CGLIB?

 

  • 當(dāng)Bean實(shí)現(xiàn)接口時(shí),Spring就會(huì)用JDK的動(dòng)態(tài)代理。
  • 當(dāng)Bean沒(méi)有實(shí)現(xiàn)接口時(shí),Spring使用CGlib實(shí)現(xiàn)。
  • 可以強(qiáng)制使用CGlib

 

責(zé)任編輯:武曉燕 來(lái)源: 月伴飛魚(yú)
相關(guān)推薦

2021-12-09 08:16:40

JVM參數(shù)系統(tǒng)

2017-08-29 11:21:03

微軟

2020-06-02 07:00:00

會(huì)話安全黑客攻擊

2020-12-23 09:00:00

開(kāi)發(fā)Web工具

2022-09-22 08:00:00

API開(kāi)發(fā)數(shù)據(jù)

2021-12-27 08:00:00

Kubernetes容器安全

2021-02-01 08:39:26

JTAG接口Jlink

2021-07-01 09:00:00

安全數(shù)字化轉(zhuǎn)型滲透

2020-08-12 09:32:31

小米MIUI

2024-07-02 11:16:21

2023-09-11 08:51:23

LinkedList雙向鏈表線程

2021-06-17 13:40:47

區(qū)塊鏈比特幣公有鏈

2020-12-08 11:08:55

時(shí)間復(fù)雜度軟件

2021-07-19 08:33:56

時(shí)間復(fù)雜度大O

2019-11-04 09:07:48

DevOps互聯(lián)網(wǎng)IT

2019-04-24 08:31:43

分布式限流kafka

2019-12-25 14:00:26

數(shù)據(jù)科學(xué)人工智能科學(xué)家

2020-03-18 18:20:19

區(qū)塊鏈數(shù)字貨幣比特幣

2019-01-24 08:19:17

云服務(wù)多云云計(jì)算

2019-07-21 08:10:21

技術(shù)研發(fā)優(yōu)化
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 婷婷中文在线 | 亚洲人成在线观看 | 亚洲精品在线视频 | 亚洲精品一区二区 | 午夜在线 | 日韩成人在线观看 | 羞羞视频在线网站观看 | 国产高清自拍视频在线观看 | 亚洲 欧美 综合 | 亚洲在线一区二区 | 久草精品视频 | 毛片视频免费 | 成人国产精品久久 | 一级片在线观看 | 国产黄a一级 | 欧美国产精品一区二区 | 亚洲一区中文字幕 | 国产欧美一区二区三区在线看 | 日本视频一区二区 | 精品免费国产一区二区三区四区介绍 | 欧美成年人网站 | 免费播放一级片 | 黄久久久 | 亚洲一区二区三区久久 | 欧美一区二区在线看 | 欧美视频在线播放 | 午夜精品久久久久久久久久久久 | 国产黄色电影 | 99爱在线免费观看 | 国产ts一区 | 91精品国产色综合久久不卡98口 | 国产精品视频综合 | 成人小视频在线免费观看 | 一区二区三区成人 | 成人福利电影 | 日日爽 | 欧美日韩在线高清 | 免费亚洲一区二区 | 成人国产精品一级毛片视频毛片 | 亚洲免费精品 | 免费看大片bbbb欧美 |