精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

動態代理竟然如此簡單!

開發 前端
動態代理在 Java 中有著廣泛的應用,比如 AOP 的實現原理、RPC遠程調用、Java 注解對象獲取、日志框架、全局性異常處理、事務處理等。

[[360336]]

這篇文章我們來聊一下 Java 中的動態代理。

動態代理在 Java 中有著廣泛的應用,比如 AOP 的實現原理、RPC遠程調用、Java 注解對象獲取、日志框架、全局性異常處理、事務處理等。

在了解動態代理前,我們需要先了解一下什么是代理模式。

代理模式

代理模式(Proxy Pattern)是 23 種設計模式的一種,屬于結構型模式。他指的是一個對象本身不做實際的操作,而是通過其他對象來得到自己想要的結果。這樣做的好處是可以在目標對象實現的基礎上,增強額外的功能操作,即擴展目標對象的功能。

這里能體現出一個非常重要的編程思想:不要隨意去改源碼,如果需要修改,可以通過代理的方式來擴展該方法。

 

如上圖所示,用戶不能直接使用目標對象,而是構造出一個代理對象,由代理對象作為中轉,代理對象負責調用目標對象真正的行為,從而把結果返回給用戶。

也就是說,代理的關鍵點就是代理對象和目標對象的關系。

代理其實就和經紀人一樣,比如你是一個明星,有很多粉絲。你的流量很多,經常會有很多金主來找你洽談合作等,你自己肯定忙不過來,因為你要處理的不只是談合作這件事情,你還要懂才藝、拍戲、維護和粉絲的關系、營銷等。為此,你找了一個經紀人,你讓他負責和金主談合作這件事,經紀人做事很認真負責,他圓滿的完成了任務,于是,金主找你談合作就變成了金主和你的經紀人談合作,你就有更多的時間來忙其他事情了。如下圖所示

 

這是一種靜態代理,因為這個代理(經紀人)是你自己親自挑選的。

但是后來隨著你的業務逐漸拓展,你無法選擇每個經紀人,所以你索性交給了代理公司來幫你做。如果你想在 B 站火一把,那就直接讓代理公司幫你找到負責營銷方面的代理人,如果你想維護和粉絲的關系,那你直接讓代理公司給你找一些托兒就可以了,那么此時的關系圖會變為如下

 

此時你幾乎所有的工作都是由代理公司來進行打理,而他們派出誰來幫你做這些事情你就不得而知了,這得根據實際情況來定,因為代理公司也不只是負責你一個明星,而且每個人所擅長的領域也不同,所以你只有等到有實際需求后,才會給你指定對應的代理人,這種情況就叫做動態代理。

靜態代理

從編譯期是否能確定最終的執行方法可以把代理模式分為靜態代理和動態代理,我們先演示一下動態代理,這里有一個需求,領導想在系統中添加一個用戶,但是他不自己添加,他讓下面的程序員來添加,我們看一下這個過程。

首先構建一個用戶接口,定義一個保存用戶的模版方法。

  1. public interface UserDao { 
  2.  
  3.     void saveUser(); 

構建一個用戶實現類,這個用戶實現類是真正進行用戶操作的方法

  1. public class UserDaoImpl implements UserDao{ 
  2.  
  3.     @Override 
  4.     public void saveUser() { 
  5.         System.out.println(" ---- 保存用戶 ---- "); 
  6.     } 

構建一個用戶代理類,用戶代理類也有一個保存用戶的方法,不過這個方法屬于代理方法,它不會執行真正的保存用戶,而是內部持有一個真正的用戶對象,進行用戶保存。

  1. public class UserProxy { 
  2.  
  3.     private UserDao userDao; 
  4.     public UserProxy(UserDao userDao){ 
  5.         this.userDao = userDao; 
  6.     } 
  7.  
  8.     public void saveUser() { 
  9.         System.out.println(" ---- 代理開始 ---- "); 
  10.         userDao.saveUser(); 
  11.         System.out.println(" ---- 代理結束 ----"); 
  12.     } 

下面是測試方法。

  1. public class UserTest { 
  2.  
  3.     public static void main(String[] args) { 
  4.  
  5.         UserDao userDao = new UserDaoImpl(); 
  6.         UserProxy userProxy = new UserProxy(userDao); 
  7.         userProxy.saveUser(); 
  8.  
  9.     } 

新創建一個用戶實現類 (UserDaoImpl),它不執行用戶操作。然后再創建一個用戶代理(UserProxy),執行用戶代理的用戶保存(saveUser),其內部會調用用戶實現類的保存用戶(saveUser)方法,因為我們 JVM 可以在編譯期確定最終的執行方法,所以上面的這種代理模式又叫做靜態代理。

代理模式具有無侵入性的優點,以后我們增加什么新功能的話,我們可以直接增加一個代理類,讓代理類來調用用戶操作,這樣我們就實現了不通過改源碼的方式增加了新的功能。然后生活很美好了,我們能夠直接添加我們想要的功能,在這美麗的日子里,cxuan 添加了用戶代理、日志代理等等無數個代理類。但是好景不長,cxuan 發現每次改代碼的時候都要改每個代理類,這就很煩啊!我寶貴的時光都浪費在改每個代理類上面了嗎?

動態代理

JDK 動態代理

于是乎 cxuan 上網求助,發現了一個叫做動態代理的概念,通讀了一下,發現有點意思,于是乎 cxuan 修改了一下靜態代理的代碼,新增了一個 UserHandler 的用戶代理,并做了一下 test,代碼如下

  1. public class UserHandler implements InvocationHandler { 
  2.  
  3.     private UserDao userDao; 
  4.  
  5.     public UserHandler(UserDao userDao){ 
  6.         this.userDao = userDao; 
  7.     } 
  8.  
  9.     @Override 
  10.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
  11.         saveUserStart(); 
  12.         Object obj = method.invoke(userDao, args); 
  13.         saveUserDone(); 
  14.         return obj; 
  15.     } 
  16.  
  17.     public void saveUserStart(){ 
  18.         System.out.println("---- 開始插入 ----"); 
  19.     } 
  20.  
  21.     public void saveUserDone(){ 
  22.         System.out.println("---- 插入完成 ----"); 
  23.     } 

測試類如下

  1. public static void dynamicProxy(){ 
  2.  
  3.   UserDao userDao = new UserDaoImpl(); 
  4.   InvocationHandler handler = new UserHandler(userDao); 
  5.  
  6.   ClassLoader loader = userDao.getClass().getClassLoader(); 
  7.   Class<?>[] interfaces = userDao.getClass().getInterfaces(); 
  8.  
  9.   UserDao proxy = (UserDao)Proxy.newProxyInstance(loader, interfaces, handler); 
  10.   proxy.saveUser(); 

UserHandler 是用戶代理類,構造函數中的 UserDao 是真實對象,通過把 UserDao 隱藏進 UserHandler ,通過 UserHandler 中的 UserDao 執行真正的方法。

類加載器、接口數組你可以把它理解為一個方法樹,每棵葉子結點都是一個方法,通過后面的 proxy.saveUser() 來告訴 JVM 執行的是方法樹上的哪個方法。

用戶代理是通過類加載器、接口數組、代理類來得到的。saveUser 方法就相當于是告訴 proxy 你最終要執行的是哪個方法,這個 proxy.saveUser 方法并不是最終直接執行的 saveUser 方法,最終的 saveUser 方法是由 UserHandler 中的 invoke 方法觸發的。

上面這種在編譯期無法確定最終的執行方法,而只能通過運行時動態獲取方法的代理模式被稱為 動態代理。

動態代理的優勢是實現無侵入式的代碼擴展,也可以對方法進行增強。此外,也可以大大減少代碼量,避免代理類泛濫成災的情況。

所以我們現在總結一下靜態代理和動態代理各自的特點。

靜態代理

  • 靜態代理類:由程序員創建或者由第三方工具生成,再進行編譯;在程序運行之前,代理類的 .class 文件已經存在了。
  • 靜態代理事先知道要代理的是什么。
  • 靜態代理類通常只代理一個類。

動態代理

  • 動態代理通常是在程序運行時,通過反射機制動態生成的。
  • 動態代理類通常代理接口下的所有類。
  • 動態代理事先不知道要代理的是什么,只有在運行的時候才能確定。
  • 動態代理的調用處理程序必須事先繼承 InvocationHandler 接口,使用 Proxy 類中的 newProxyInstance 方法動態的創建代理類。

在上面的代碼示例中,我們是定義了一個 UserDao 接口,然后有 UserDaoImpl 接口的實現類,我們通過 Proxy.newProxyInstance 方法得到的也是 UserDao 的實現類對象,那么其實這是一種基于接口的動態代理。也叫做 JDK 動態代理。

是不是只有這一種動態代理技術呢?既然都這么問了,那當然不是。

除此之外,還有一些其他代理技術,不過是需要加載額外的 jar 包的,那么我們匯總一下所有的代理技術和它的特征

  • JDK 的動態代理使用簡單,它內置在 JDK 中,因此不需要引入第三方 Jar 包。
  • CGLIB 和 Javassist 都是高級的字節碼生成庫,總體性能比 JDK 自帶的動態代理好,而且功能十分強大。
  • ASM 是低級的字節碼生成工具,使用 ASM 已經近乎于在使用字節碼編程,對開發人員要求最高。當然,也是性能最好的一種動態代理生成工具。但 ASM 的使用很繁瑣,而且性能也沒有數量級的提升,與 CGLIB 等高級字節碼生成工具相比,ASM 程序的維護性較差,如果不是在對性能有苛刻要求的場合,還是推薦 CGLIB 或者 Javassist。

下面我們就來依次介紹一下這些動態代理工具的使用

CGLIB 動態代理

上面我們提到 JDK 動態代理是基于接口的代理,而 CGLIB 動態代理是針對類實現代理,主要是對指定的類生成一個子類,覆蓋其中的方法 ,也就是說 CGLIB 動態代理采用類繼承 -> 方法重寫的方式進行的,下面我們先來看一下 CGLIB 動態代理的結構。

 

如上圖所示,代理類繼承于目標類,每次調用代理類的方法都會在攔截器中進行攔截,攔截器中再會調用目標類的方法。

下面我們通過一個示例來演示一下 CGLIB 動態代理的使用

首先導入 CGLIB 相關 jar 包,我們使用的是 MAVEN 的方式

  1. <dependency> 
  2.   <groupId>cglib</groupId> 
  3.   <artifactId>cglib</artifactId> 
  4.   <version>3.2.5</version> 
  5. </dependency> 

然后我們新創建一個 UserService 類,為了和上面的 UserDao 和 UserDaoImpl 進行區分。

  1. public class UserService { 
  2.    public void saveUser(){ 
  3.        System.out.println("---- 保存用戶 ----"); 
  4.    } 

之后我們創建一個自定義方法攔截器,這個自定義方法攔截器實現了攔截器類

  1. public class AutoMethodInterceptor implements MethodInterceptor { 
  2.  
  3.     public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { 
  4.         System.out.println("---- 方法攔截 ----"); 
  5.         Object object = methodProxy.invokeSuper(obj, args); 
  6.         return object; 
  7.     } 

這里解釋一下這幾個參數都是什么含義

  • Object obj: obj 是 CGLIB 動態生成代理類實例
  • Method method: Method 為實體類所調用的被代理的方法引用
  • Objectp[] args: 這個就是方法的參數列表
  • MethodProxy methodProxy : 這個就是生成的代理類對方法的引用。

對于 methodProxy 參數調用的方法,在其內部有兩種選擇:invoke() 和 invokeSuper() ,二者的區別不在本文展開說明,感興趣的讀者可以參考本篇文章:Cglib源碼分析 invoke和invokeSuper的差別

然后我們創建一個測試類進行測試

  1. public static void main(String[] args) { 
  2.  
  3.   Enhancer enhancer = new Enhancer(); 
  4.   enhancer.setSuperclass(UserService.class); 
  5.   enhancer.setCallback(new AutoMethodInterceptor()); 
  6.  
  7.   UserService userService = (UserService)enhancer.create(); 
  8.  
  9.   userService.saveUser(); 

測試類主要涉及 Enhancer 的使用,Enhancer 是一個非常重要的類,它允許為非接口類型創建一個 Java 代理,Enhancer 動態的創建給定類的子類并且攔截代理類的所有的方法,和 JDK 動態代理不一樣的是不管是接口還是類它都能正常工作。

JDK 動態代理與 CGLIB 動態代理都是將真實對象隱藏在代理對象的后面,以達到 代理 的效果。與 JDK 動態代理所不同的是 CGLIB 動態代理使用 Enhancer 來創建代理對象,而 JDK 動態代理使用的是 Proxy.newProxyInstance 來創建代理對象;還有一點是 CGLIB 可以代理大部分類,而 JDK 動態代理只能代理實現了接口的類。

Javassist 代理

Javassist是在 Java 中編輯字節碼的類庫;它使 Java 程序能夠在運行時定義一個新類, 并在 JVM 加載時修改類文件。我們使用最頻繁的動態特性就是 反射,而且反射也是動態代理的基礎,我們之所以沒有提反射對動態代理的作用是因為我想在后面詳聊,反射可以在運行時查找對象屬性、方法,修改作用域,通過方法名稱調用方法等。實時應用不會頻繁使用反射來創建,因為反射開銷比較大,另外,還有一種具有和反射一樣功能強大的特性那就是 Javaassist。

我們先通過一個簡單的示例來演示一下 Javaassist ,以及 Javaassist 如何創建動態代理。

我們仍舊使用上面提到的 UserDao 和 UserDaoImpl 作為基類。

我們新創建一個 AssistByteCode 類,它里面有一個 createByteCode 方法,這個方法主要做的事情就是通過字節碼生成 UserDaoImpl 實現類。我們下面來看一下它的代碼

  1. public class AssistByteCode { 
  2.  
  3.     public static void createByteCode() throws Exception{ 
  4.         ClassPool classPool = ClassPool.getDefault(); 
  5.         CtClass cc = classPool.makeClass("com.cxuan.proxypattern.UserDaoImpl"); 
  6.  
  7.         // 設置接口 
  8.         CtClass ctClass = classPool.get("com.cxuan.proxypattern.UserDao"); 
  9.         cc.setInterfaces(new CtClass[] {ctClass}); 
  10.  
  11.         // 創建方法 
  12.         CtMethod saveUser = CtMethod.make("public void saveUser(){}", cc); 
  13.         saveUser.setBody("System.out.println(\"---- 插入用戶 ----\");"); 
  14.         cc.addMethod(saveUser); 
  15.  
  16.         Class c = cc.toClass(); 
  17.         cc.writeFile("/Users/mr.l/cxuan-justdoit"); 
  18.  
  19.     } 

由于本文并不是一個具體研究 Javaassist 的文章,所以我們不會過多研究細節問題,只專注于這個框架一些比較重要的類

ClassPool:ClassPool 就是一個 CtClass 的容器,而一個 CtClass 對象就是一個 class 對象的實例,這個實例和 class 對象一樣,包含屬性、方法等。

那么上面代碼主要做了哪些事兒呢?通過 ClassPool 來獲取 CtClass 所需要的接口、抽象類的 CtClass 實例,然后通過 CtClass 實例添加自己的屬性和方法,并通過它的 writeFile 把二進制流輸出到當前項目的根目錄路徑下。writeFile 其內部是使用了 DataOutputStream 進行輸出的。

流寫完后,我們打開這個 .class 文件如下所示

  1. public class UserDaoImpl implements UserDao { 
  2.     public void saveUser() { 
  3.         System.out.println("---- 插入用戶 ----"); 
  4.     } 
  5.  
  6.     public UserDaoImpl() { 
  7.     } 

可以對比一下上面發現 UserDaoImpl 發現編譯器除了為我們添加了一個公有的構造器,其他基本一致。

 

經過這個簡單的示例后,cxuan 給你演示一下如何使用 Javaassist 動態代理。

首先我們先創建一個 Javaassist 的代理工廠,代碼如下

  1. public class JavaassistProxyFactory { 
  2.  
  3.     public Object getProxy(Class clazz) throws Exception{ 
  4.  
  5.         // 代理工廠 
  6.         ProxyFactory proxyFactory = new ProxyFactory(); 
  7.         // 設置需要創建的子類 
  8.         proxyFactory.setSuperclass(clazz); 
  9.         proxyFactory.setHandler((self, thisMethod, proceed, args) -> { 
  10.  
  11.             System.out.println("---- 開始攔截 ----"); 
  12.             Object result = proceed.invoke(self, args); 
  13.             System.out.println("---- 結束攔截 ----"); 
  14.  
  15.             return result; 
  16.         }); 
  17.         return proxyFactory.createClass().newInstance(); 
  18.  
  19.     } 

上面我們定義了一個代理工廠,代理工廠里面創建了一個 handler,在調用目標方法時,Javassist 會回調 MethodHandler 接口方法攔截,來調用真正執行的方法,你可以在攔截方法的前后實現自己的業務邏輯。最后的 proxyFactory.createClass().newInstance() 就是使用字節碼技術來創建了最終的子類實例,這種代理方式類似于 JDK 中的 InvocationHandler 接口。

測試方法如下

  1. public static void main(String[] args) throws Exception { 
  2.  
  3.   JavaassistProxyFactory proxyFactory = new JavaassistProxyFactory(); 
  4.   UserService userProxy = (UserService) proxyFactory.getProxy(UserService.class); 
  5.   userProxy.saveUser(); 

ASM 代理

ASM 是一套 Java 字節碼生成架構,它可以動態生成二進制格式的子類或其它代理類,或者在類被 Java 虛擬機裝入內存之前,動態修改類。

下面我們使用 ASM 框架實現一個動態代理,ASM 生成的動態代理

以下代碼摘自 https://blog.csdn.net/lightj1996/article/details/107305662

  1. public class AsmProxy extends ClassLoader implements Opcodes { 
  2.  
  3.     public static void createAsmProxy() throws Exception { 
  4.  
  5.         // 目標類類名 字節碼中類修飾符以 “/” 分割 
  6.         String targetServiceName = TargetService.class.getName().replace(".""/"); 
  7.         // 切面類類名 
  8.         String aspectServiceName = AspectService.class.getName().replace(".""/"); 
  9.         // 代理類類名 
  10.         String proxyServiceName = targetServiceName+"Proxy"
  11.         // 創建一個 classWriter 它是繼承了ClassVisitor 
  12.         ClassWriter classWriter = new ClassWriter(0); 
  13.         // 訪問類 指定jdk版本號為1.8, 修飾符為 public,父類是TargetService 
  14.         classWriter.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, proxyServiceName, null, targetServiceName, null); 
  15.         // 訪問目標類成員變量 為類添加切面屬性 “private TargetService targetService” 
  16.         classWriter.visitField(ACC_PRIVATE, "targetService""L" + targetServiceName+";"nullnull); 
  17.         // 訪問切面類成員變量 為類添加目標屬性 “private AspectService aspectService” 
  18.         classWriter.visitField(ACC_PRIVATE, "aspectService""L" + aspectServiceName+";"nullnull); 
  19.  
  20.         // 訪問默認構造方法 TargetServiceProxy() 
  21.         // 定義函數 修飾符為public 方法名為 <init>, 方法表述符為()V 表示無參數,無返回參數 
  22.         MethodVisitor initVisitor = classWriter.visitMethod(ACC_PUBLIC, "<init>""()V"nullnull); 
  23.         // 從局部變量表取第0個元素 “this” 
  24.         initVisitor.visitVarInsn(ALOAD, 0); 
  25.         // 調用super 的構造方法 invokeSpecial在這里的意思是調用父類方法 
  26.         initVisitor.visitMethodInsn(INVOKESPECIAL, targetServiceName, "<init>""()V"false); 
  27.         // 方法返回 
  28.         initVisitor.visitInsn(RETURN); 
  29.         // 設置最大棧數量,最大局部變量表數量 
  30.         initVisitor.visitMaxs(1, 1); 
  31.         // 訪問結束 
  32.         initVisitor.visitEnd(); 
  33.  
  34.         // 創建有參構造方法 TargetServiceProxy(TargetService var1, AspectService var2) 
  35.         // 定義函數 修飾符為public 方法名為 <init>, 方法表述符為(TargetService, AspectService)V 表示無參數,無返回參數 
  36.         MethodVisitor methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "<init>""(L" + targetServiceName + ";L"+aspectServiceName+";)V"nullnull); 
  37.         // 從局部變量表取第0個元素 “this”壓入棧頂 
  38.         methodVisitor.visitVarInsn(ALOAD, 0); 
  39.         // this出棧 , 調用super 的構造方法 invokeSpecial在這里的意思是調用父類方法。 <init>的owner是AspectService, 無參無返回類型 
  40.         methodVisitor.visitMethodInsn(INVOKESPECIAL, targetServiceName, "<init>""()V"false); 
  41.         // 從局部變量表取第0個元素 “this”壓入棧頂 
  42.         methodVisitor.visitVarInsn(ALOAD, 0); 
  43.         // 從局部變量表取第1個元素 “targetService”壓入棧頂 
  44.         methodVisitor.visitVarInsn(ALOAD, 1); 
  45.         // this 和 targetService 出棧, 調用targetService put 賦值給this.targetService 
  46.         methodVisitor.visitFieldInsn(PUTFIELD, proxyServiceName, "targetService""L" + targetServiceName + ";"); 
  47.         // 從局部變量表取第0個元素 “this”壓入棧頂 
  48.         methodVisitor.visitVarInsn(ALOAD, 0); 
  49.         // 從局部變量表取第2個元素 “aspectService”壓入棧頂 
  50.         methodVisitor.visitVarInsn(ALOAD, 2); 
  51.         // this 和 aspectService 出棧 將 targetService put 賦值給this.aspectService 
  52.         methodVisitor.visitFieldInsn(PUTFIELD, proxyServiceName, "aspectService""L" + aspectServiceName + ";"); 
  53.         // 方法返回 
  54.         methodVisitor.visitInsn(RETURN); 
  55.         // 設置最大棧數量,最大局部變量表數量 
  56.         methodVisitor.visitMaxs(2, 3); 
  57.         // 方法返回 
  58.         methodVisitor.visitEnd(); 
  59.  
  60.         // 創建代理方法 修飾符為public,方法名為 demoQuest 
  61.         MethodVisitor visitMethod = classWriter.visitMethod(ACC_PUBLIC, "demoQuest""()I"nullnull); 
  62.         // 從局部變量表取第0個元素 “this”壓入棧頂 
  63.         visitMethod.visitVarInsn(ALOAD, 0); 
  64.         // this 出棧 將this.aspectService壓入棧頂 
  65.         visitMethod.visitFieldInsn(GETFIELD, proxyServiceName, "aspectService""L"+aspectServiceName+";"); 
  66.         // 取棧頂元素出棧 也就是targetService 調用其preOperation方法, demoQuest的owner是AspectService, 無參無返回類型 
  67.         visitMethod.visitMethodInsn(INVOKEVIRTUAL, aspectServiceName,"preOperation""()V"false); 
  68.         // 從局部變量表取第0個元素 “this”壓入棧頂 
  69.         visitMethod.visitVarInsn(ALOAD, 0); 
  70.         // this 出棧, 取this.targetService壓入棧頂 
  71.         visitMethod.visitFieldInsn(GETFIELD, proxyServiceName, "targetService""L"+targetServiceName+";"); 
  72.         // 取棧頂元素出棧 也就是targetService調用其demoQuest方法, demoQuest的owner是TargetService, 無參無返回類型 
  73.         visitMethod.visitMethodInsn(INVOKEVIRTUAL, targetServiceName, "demoQuest""()I"false); 
  74.         // 方法返回 
  75.         visitMethod.visitInsn(IRETURN); 
  76.         // 設置最大棧數量,最大局部變量表數量 
  77.         visitMethod.visitMaxs(1, 1); 
  78.         // 方法返回 
  79.         visitMethod.visitEnd(); 
  80.  
  81.         // 生成字節碼二進制流 
  82.         byte[] code = classWriter.toByteArray(); 
  83.         // 自定義classloader加載類 
  84.         Class<?> clazz = (new AsmProxy()).defineClass(TargetService.class.getName() + "Proxy", code, 0, code.length); 
  85.         // 取其帶參數的構造方法 
  86.         Constructor constructor = clazz.getConstructor(TargetService.class, AspectService.class); 
  87.         // 使用構造方法實例化對象 
  88.         Object object = constructor.newInstance(new TargetService(), new AspectService()); 
  89.  
  90.         // 使用TargetService類型的引用接收這個對象 
  91.         TargetService targetService; 
  92.         if (!(object instanceof TargetService)) { 
  93.             return
  94.         } 
  95.         targetService = (TargetService)object; 
  96.  
  97.         System.out.println("生成代理類的名稱: " + targetService.getClass().getName()); 
  98.         // 調用被代理方法 
  99.         targetService.demoQuest(); 
  100.  
  101.         // 這里可以不用寫, 但是如果想看最后生成的字節碼長什么樣子,可以寫 "ascp-purchase-app/target/classes/"是我的根目錄, 閱讀者需要將其替換成自己的 
  102.         String classPath = "/Users/mr.l/cxuan-justdoit/"
  103.         String path = classPath + proxyServiceName + ".class"
  104.         FileOutputStream fos = 
  105.                 new FileOutputStream(path); 
  106.         fos.write(code); 
  107.         fos.close(); 
  108.  
  109.     } 

使用 ASM 生成動態代理的代碼比較長,上面這段代碼的含義就是生成類 TargetServiceProxy,用于代理TargetService ,在調用 targetService.demoQuest() 方法之前調用切面的方法 aspectService.preOperation();

測試類就直接調用 AsmProxy.createAsmProxy() 方法即可,比較簡單。

下面是我們生成 TargetServiceProxy 的目標類

 

至此,我們已經介紹了四種動態代理的方式,分別是JDK 動態代理、CGLIB 動態代理、Javaassist 動態代理、ASM 動態代理,那么現在思考一個問題,為什么會有動態代理的出現呢?或者說動態代理是基于什么原理呢?

其實我們上面已經提到過了,沒錯,動態代理使用的就是反射 機制,反射機制是 Java 語言提供的一種基礎功能,􏱥􏱩賦予程序在運行時動態修改屬性、方法的能力。通過反射我們能夠直接操作類或者對象,比如獲取某個類的定義,獲取某個類的屬性和方法等。

關于 Java 反射的相關內容可以參考 Java建設者的這一篇文章

給女同事講完代理后,女同事說:你好棒哦

另外還有需要注意的一點,從性能角度來講,有些人得出結論說是 Java 動態代理要比 CGLIB 和 Javaassist 慢幾十倍,其實,在主流 JDK 版本中,Java 動態代理可以提供相等的性能水平,數量級的差距不是廣泛存在的。而且,在現代 JDK 中,反射已經得到了改進和優化。

 

我們在選型中,性能考量并不是主要關注點,可靠性、可維護性、編碼工作量同等重要。

本文轉載自微信公眾號「 Java建設者」,可以通過以下二維碼關注。轉載本文請聯系 Java建設者公眾號。

 

責任編輯:武曉燕 來源: Java建設者
相關推薦

2024-12-03 08:43:49

2021-12-08 10:36:46

JavaPDF文件

2021-12-09 09:02:53

JavaPDF文件iText

2020-06-19 17:49:23

建網

2018-08-27 08:31:25

InnoDBMySQL

2025-11-14 03:00:00

MySQL并發數據

2020-02-20 16:07:45

IT需求

2012-02-08 10:12:19

Java反射

2022-02-23 20:42:40

HTMLmarkdownturndown

2022-01-09 23:38:42

通信協議網絡

2023-12-06 08:23:44

代理模式設計模式

2017-05-11 21:30:01

Android動態代理ServiceHook

2022-07-08 14:35:05

Java組件LiteFlow

2010-08-25 21:50:36

配置DHCP

2022-08-12 12:19:13

Cluster檢索集群

2011-04-06 11:41:25

Java動態代理

2023-04-06 00:11:12

Java接口開發

2011-10-11 10:53:29

Ubuntu 11.1Gnome 3.2

2012-08-28 10:59:26

JavaJava動態代理Proxy

2009-04-29 01:39:57

破解美萍萬象
點贊
收藏

51CTO技術棧公眾號

国产精品伦一区二区三区| 亚洲美女av网站| 欧美日韩成人在线观看| 哪个网站能看毛片| 丰满人妻一区二区三区四区53| 亚欧洲精品视频在线观看| 精品一区二区av| 在线观看中文字幕亚洲| 欧美性大战久久久久xxx| 亚洲精品一区二区三区不卡| 欧美韩国一区| 91精品国产综合久久福利软件| 欧美福利一区二区三区| 日韩成人在线免费视频| 成人动态视频| 亚洲图片欧美视频| 波多野结衣成人在线| 青青操国产视频| 国产日韩在线观看视频| 亚洲色图一区二区| 亚洲资源在线看| 国产高潮流白浆| 亚洲欧洲一二区| 中文字幕一区二区三区乱码在线| 国产精品电影一区| 手机看片福利视频| 成人亚洲视频| 中文字幕一区二区三区不卡| 国产一区二区三区免费不卡| 美日韩一二三区| 中日韩免视频上线全都免费| 色中色一区二区| 日韩国产欧美精品| 亚洲一区 中文字幕| 国产精品99久久精品| 91精品国产高清一区二区三区蜜臀| 欧美亚洲一二三区| 国产最新视频在线观看| 精品亚洲成人| 欧美日韩在线播| 一区二区三区四区在线视频| 国产三级精品在线观看| 国内自拍视频一区二区三区| 337p日本欧洲亚洲大胆色噜噜| 国产二区视频在线| 欧美黄色小说| 久久成人精品无人区| 欧美成人手机在线| 国产+高潮+白浆+无码| 26uuu亚洲电影| 国产精品福利影院| 午夜精品一区二区三区四区| 999免费视频| 加勒比av一区二区| 成人精品久久一区二区三区| 国产大片中文字幕| 国产欧美亚洲精品a| 欧美精品少妇一区二区三区| 日韩精品一区二区免费| 久久久久国产精品嫩草影院| av亚洲精华国产精华| 国产成人涩涩涩视频在线观看| 欧美福利在线视频| 高清欧美性猛交xxxx黑人猛| 欧美伊人久久大香线蕉综合69 | 久久精品论坛| 在线观看中文字幕不卡| 永久免费网站视频在线观看| 天天综合永久入口| 麻豆成人久久精品二区三区小说| 色综合色综合久久综合频道88| 中文字幕一区二区三区人妻不卡| 国产精品亚洲欧美一级在线| 91麻豆精品国产无毒不卡在线观看| 你懂的av在线| av网站网址在线观看| 91免费看`日韩一区二区| 国产在线观看91精品一区| 韩国av免费观看| 一本一道久久综合狠狠老 | 十九岁完整版在线观看好看云免费| 日本一不卡视频| 国外成人在线视频| 日韩视频中文字幕在线观看| 亚洲第一偷拍| 深夜成人在线观看| 少妇毛片一区二区三区| 精品香蕉视频| 欧美精品免费在线| 在线精品免费视| 99精品视频免费观看视频| 操人视频在线观看欧美| 男人天堂资源网| 1024精品久久久久久久久| 久久久亚洲福利精品午夜| 四虎成人在线观看| 国产精一品亚洲二区在线视频| 国产精品一区二区久久久久| 亚洲 国产 日韩 欧美| 亚洲综合社区| 97在线视频免费看| 国产精品www爽爽爽| 欧美在线关看| 亚洲成人性视频| xxxxwww一片| 日韩视频一区二区三区四区| 91精品国产综合久久精品性色| 亚洲少妇一区二区三区| 亚州一区二区| 伊人久久男人天堂| 亚洲黄色三级视频| 国产麻豆视频精品| 韩国成人av| 亚洲欧美丝袜中文综合| 不卡的av电影| 黑人中文字幕一区二区三区| 美女写真理伦片在线看| 中文字幕在线观看不卡| 春日野结衣av| 日韩一级淫片| 久久亚洲国产精品| 丰满人妻一区二区三区四区| 蜜桃视频免费观看一区| 国产精品揄拍500视频| 婷婷综合激情网| 夜色激情一区二区| 91成人在线观看喷潮教学| 亚洲午夜剧场| 中文字幕无线精品亚洲乱码一区 | 欧美日韩裸体免费视频| 亚洲欧洲日产国码无码久久99| 日本一区二区三区视频在线看 | 六月婷婷色综合| 欧洲亚洲一区二区| 春暖花开成人亚洲区| 国产欧美1区2区3区| 亚洲图片都市激情| 91小视频xxxx网站在线| 欧美日韩精品一区二区三区| 亚洲一级片免费观看| 97久久综合精品久久久综合| 日韩综合中文字幕| 69精品久久久| 国产成人免费视| 欧美另类网站| 91久久国产综合久久91猫猫| 亚洲精品av在线| 美国精品一区二区| 日韩av在线播放中文字幕| 欧美aaaaa喷水| 97caopor国产在线视频| 欧美一三区三区四区免费在线看| 日本视频在线免费| 九一久久久久久| 黄瓜视频免费观看在线观看www| 欧美人与禽猛交乱配| 动漫精品一区二区| av2014天堂网| 久久精品影视| 91牛牛免费视频| 久久久久久青草| 在线观看国产精品网站| 中文字幕伦理片| 九一久久久久久| 精品人妻人人做人人爽| 另类ts人妖一区二区三区| 97超碰蝌蚪网人人做人人爽| 免费成人av电影| 欧美视频在线观看一区| 日本一二三区在线观看| 免费日韩精品中文字幕视频在线| 另类欧美小说| 日韩一级二级| 亚洲精品中文字幕有码专区| 1024手机在线视频| 成人午夜激情片| 国产免费一区二区三区四在线播放| 性感女国产在线| 欧美一区三区二区| 日韩高清精品免费观看| 久久久夜色精品亚洲| av免费看网址| 国产成人ay| 91久久国产婷婷一区二区| 日韩av官网| 91精品国产91久久久久久最新毛片 | 97在线国产视频| 国产精品一区免费在线| 久久久在线视频| 97电影在线| 色av一区二区| 人妻少妇精品视频一区二区三区| 欧美成人有码| 美女三级99| crdy在线观看欧美| 欧美性在线观看| 图片区 小说区 区 亚洲五月| 色94色欧美sute亚洲线路二 | 亚洲免费观看视频| 五月激情五月婷婷| 成人在线视频免费观看| 日本乱人伦a精品| 青青草娱乐在线| 欧美午夜电影在线| 六十路息与子猛烈交尾| 美女脱光内衣内裤视频久久影院| 99热久久这里只有精品| 日韩欧美自拍| 91在线观看免费观看| 国产高清不卡| 欧美极品美女电影一区| 你懂的网站在线| 亚洲成a人片在线观看中文| 香蕉久久久久久av成人| 日韩av一级片| av免费中文字幕| 好吊日精品视频| 伊人久久大香线蕉精品| 国产99亚洲| 国产精品亚发布| 极品在线视频| 亚洲欧洲视频在线| 国产精品成人无码| 五月天网站亚洲| 真实乱视频国产免费观看 | 一级日本不卡的影视| 蜜桃av免费观看| 久久精品人人做| 777一区二区| 久久激情网站| 一区二区在线不卡| 国产精品手机在线播放| 精品国产一区二区三区麻豆小说 | 一区二区高清视频| 狠狠综合久久av一区二区蜜桃 | 国产日本欧美视频| 久久久人成影片一区二区三区在哪下载| 亚洲嫩模很污视频| 天堂中文网在线| 亚洲国产精品va在线观看黑人| av免费观看在线| 精品日韩中文字幕| 中文字幕一区二区三区手机版| 一个色在线综合| 玖玖爱免费视频| 久久网站最新地址| 在线黄色免费观看| 国产一区久久| 欧美黄色免费网址| 国产欧美日韩精品一区二区免费| 国产私拍一区| 亚洲免费专区| 青青草国产精品| 第一社区sis001原创亚洲| 亚洲一区二区三区精品动漫| 久久在线播放| 国产精品乱码| av成人在线播放| 国产裸体写真av一区二区| 91亚洲精品在看在线观看高清| 成人黄色免费片| 一区二区亚洲视频| 国产精品亚洲美女av网站| 久久精品黄色| 欧美一级在线播放| 欧美与亚洲与日本直播| 国产欧美一区二区三区视频| 成人av在线播放| 国产精品制服诱惑| 在线看成人短视频| 在线观看成人av电影| 国产精品大片免费观看| 91视频最新入口| 美腿丝袜亚洲一区| 伊人av在线播放| 久久国产麻豆精品| 国产福利精品一区二区三区| 美女黄色成人网| 91人人澡人人爽人人精品| 亚洲国产专区| 亚洲欧美在线网| 天堂av一区二区三区在线播放 | 国产精品二线| 亚洲精品久久久久久久久久久久久| 亚洲欧美日韩动漫| 中文字幕日韩专区| www视频在线观看| 国产日韩中文字幕在线| 丁香5月婷婷久久| 亚洲国产一区二区三区在线| 国产精品草草| 天天综合网日韩| av中文字幕一区| 国产精品99久久久久久成人| 婷婷国产在线综合| 国产美女www爽爽爽视频| 日韩成人中文电影| 五月婷婷六月激情| 久久精品国产99国产精品澳门| 国产视频网址在线| 九色精品美女在线| 亚洲国产尤物| 精品久久久久久综合日本| 91青青国产在线观看精品| 国产精品秘入口18禁麻豆免会员| 精品中文字幕一区二区小辣椒| 亚洲制服丝袜在线播放| 亚洲欧洲日本在线| 日本高清一二三区| 色就色 综合激情| 少妇又色又爽又黄的视频| 久久精品国产免费观看| 日韩免费va| 国产女同一区二区| 四虎5151久久欧美毛片| 日本一道在线观看| 精品伊人久久久久7777人| 亚洲久久久久久久| 精品久久久久久久久久ntr影视| 国产内射老熟女aaaa∵| 中文字幕亚洲综合| free欧美| 日韩av电影免费在线观看| 一本久道久久综合婷婷鲸鱼| 人妻精油按摩bd高清中文字幕| 国产日产欧美一区| 黄色av网站免费观看| 欧美性生活久久| 三级av在线| 欧美性受xxx| 日本亚洲不卡| 久久久999免费视频| 成人精品国产一区二区4080| www青青草原| 91精品欧美综合在线观看最新 | 欧美精品videosex极品1| 国产精品一区二区美女视频免费看| 一区二区三区三区在线| 青青国产91久久久久久| x88av在线| 欧美在线一区二区| 成人精品一区二区三区免费| 日韩美女免费观看| gogogo高清在线观看一区二区| 国产一区亚洲二区三区| 久久99精品国产.久久久久久| www.日本高清视频| 欧美揉bbbbb揉bbbbb| 在线观看美女网站大全免费| 国产精品视频区| 精品国产18久久久久久洗澡| 欧洲精品在线播放| 波多野结衣一区二区三区| 91九色丨porny丨肉丝| 亚洲欧洲一区二区三区久久| 桃子视频成人app| 亚洲精品在线免费看| 激情综合色综合久久综合| 东方av正在进入| 精品动漫一区二区三区在线观看| heyzo中文字幕在线| 国产精品一二三在线| 欧美r级电影| 深夜视频在线观看| 精品久久久久久久久久久久| 男同在线观看| 国产欧美久久一区二区| 午夜久久久久| 内射中出日韩无国产剧情| 欧美无人高清视频在线观看| 黄av在线免费观看| 国产精品99久久久久久人 | 日韩有码片在线观看| 国产精品一站二站| 国产不卡一区二区视频| 国产亚洲一本大道中文在线| 91久久久久久久久久久久| 亚洲裸体xxxx| 草民电影神马电影一区二区| 日本a级片在线观看| 91一区二区三区在线观看| 欧美日韩大片在线观看| 亚洲国产一区二区三区在线观看 | 蜜桃传媒一区二区亚洲| 欧美放荡的少妇| 国产传媒在线| 一本久道久久综合| heyzo一本久久综合| 中文字幕欧美人妻精品| 亚洲视频axxx| 精品国产亚洲一区二区三区在线| 亚洲成人蜜桃| 国产69精品一区二区亚洲孕妇| 我要看黄色一级片| 日韩av网址在线| 成人污版视频| 欧美国产日韩在线播放| 久久久久国产精品麻豆ai换脸 | 欧美色片在线观看| 国产高清不卡无码视频|