自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

除了JDK、CGLIB,還有3種類代理方式

開發(fā) 前端
不出意外,你可能只知道兩種類代理的方式。一種是JDK自帶的,另外一種是CGLIB。

 五種類代理的方式

不出意外,你可能只知道兩種類代理的方式。一種是JDK自帶的,另外一種是CGLIB。

我們先定義出一個接口和相應(yīng)的實(shí)現(xiàn)類,方便后續(xù)使用代理類在方法中添加輸出信息。

「定義接口」

  1. public interface IUserApi { 
  2.  
  3.     String queryUserInfo(); 
  4.  

「實(shí)現(xiàn)接口」

  1. public class UserApi implements IUserApi { 
  2.  
  3.     public String queryUserInfo() { 
  4.         return "沉淀、分享、成長,讓自己和他人都能有所收獲!"
  5.     } 
  6.  

好!接下來我們就給這個類方法使用代理加入一行額外輸出的信息。

0. 先補(bǔ)充一點(diǎn)反射的知識

  1. @Test 
  2. public void test_reflect() throws Exception { 
  3.     Class<UserApi> clazz = UserApi.class
  4.     Method queryUserInfo = clazz.getMethod("queryUserInfo"); 
  5.     Object invoke = queryUserInfo.invoke(clazz.newInstance()); 
  6.     System.out.println(invoke); 
  • 點(diǎn)評:有代理地方幾乎就會有反射,他們是一套互相配合使用的功能類。在反射中可以調(diào)用方法、獲取屬性、拿到注解等相關(guān)內(nèi)容。這些都可以與接下來的類代理組合使用,完成各種框架中的技術(shù)場景。

1. JDK代理方式

  1. public class JDKProxy { 
  2.  
  3.     public static <T> T getProxy(Class clazz) throws Exception { 
  4.         ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 
  5.         return (T) Proxy.newProxyInstance(classLoader, new Class[]{clazz}, new InvocationHandler() { 
  6.             public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
  7.                 System.out.println(method.getName() + " 你被代理了,By JDKProxy!"); 
  8.                 return "沉淀、分享、成長,讓自己和他人都能有所收獲!"
  9.             } 
  10.         }); 
  11.     } 
  12.  
  13.  
  14. @Test 
  15. public void test_JDKProxy() throws Exception { 
  16.     IUserApi userApi = JDKProxy.getProxy(IUserApi.class); 
  17.     String invoke = userApi.queryUserInfo(); 
  18.     logger.info("測試結(jié)果:{}", invoke); 
  19.  
  20. /** 
  21.  * 測試結(jié)果: 
  22.  *  
  23.  * queryUserInfo 你被代理了,By JDKProxy! 
  24.  * 19:55:47.319 [main] INFO  org.itstack.interview.test.ApiTest - 測試結(jié)果: 沉淀、分享、成長,讓自己和他人都能有所收獲! 
  25.  * 
  26.  * Process finished with exit code 0 
  27.  */ 

 

  • 指數(shù):⭐⭐
  • 場景:中間件開發(fā)、設(shè)計(jì)模式中代理模式和裝飾器模式應(yīng)用
  • 點(diǎn)評:這種JDK自帶的類代理方式是非常常用的一種,也是非常簡單的一種。基本會在一些中間件代碼里看到例如:數(shù)據(jù)庫路由組件、Redis組件等,同時我們也可以使用這樣的方式應(yīng)用到設(shè)計(jì)模式中。

2. CGLIB代理方式

  1. public class CglibProxy implements MethodInterceptor { 
  2.     public Object newInstall(Object object) { 
  3.         return Enhancer.create(object.getClass(), this); 
  4.     } 
  5.     public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { 
  6.         System.out.println("我被CglibProxy代理了"); 
  7.         return methodProxy.invokeSuper(o, objects); 
  8.     } 
  9.  
  10. @Test 
  11. public void test_CglibProxy() throws Exception { 
  12.     CglibProxy cglibProxy = new CglibProxy(); 
  13.     UserApi userApi = (UserApi) cglibProxy.newInstall(new UserApi()); 
  14.     String invoke = userApi.queryUserInfo(); 
  15.     logger.info("測試結(jié)果:{}", invoke); 
  16.  
  17. /** 
  18.  * 測試結(jié)果: 
  19.  *  
  20.  * queryUserInfo 你被代理了,By CglibProxy! 
  21.  * 19:55:47.319 [main] INFO  org.itstack.interview.test.ApiTest - 測試結(jié)果:  沉淀、分享、成長,讓自己和他人都能有所收獲! 
  22.  * 
  23.  * Process finished with exit code 0 
  24.  */ 

 

  • 場景:Spring、AOP切面、鑒權(quán)服務(wù)、中間件開發(fā)、RPC框架等
  • 點(diǎn)評:CGLIB不同于JDK,它的底層使用ASM字節(jié)碼框架在類中修改指令碼實(shí)現(xiàn)代理,所以這種代理方式也就不需要像JDK那樣需要接口才能代理。同時得益于字節(jié)碼框架的使用,所以這種代理方式也會比使用JDK代理的方式快1.5~2.0倍。

3. ASM代理方式

  1. public class ASMProxy extends ClassLoader { 
  2.  
  3.     public static <T> T getProxy(Class clazz) throws Exception { 
  4.  
  5.         ClassReader classReader = new ClassReader(clazz.getName()); 
  6.         ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_MAXS); 
  7.  
  8.         classReader.accept(new ClassVisitor(ASM5, classWriter) { 
  9.             @Override 
  10.             public MethodVisitor visitMethod(int access, final String name, String descriptor, String signature, String[] exceptions) { 
  11.  
  12.                 // 方法過濾 
  13.                 if (!"queryUserInfo".equals(name)) 
  14.                     return super.visitMethod(access, name, descriptor, signature, exceptions); 
  15.  
  16.                 final MethodVisitor methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions); 
  17.  
  18.                 return new AdviceAdapter(ASM5, methodVisitor, access, name, descriptor) { 
  19.  
  20.                     @Override 
  21.                     protected void onMethodEnter() { 
  22.                         // 執(zhí)行指令;獲取靜態(tài)屬性 
  23.                         methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System""out""Ljava/io/PrintStream;"); 
  24.                         // 加載常量 load constant 
  25.                         methodVisitor.visitLdcInsn(name + " 你被代理了,By ASM!"); 
  26.                         // 調(diào)用方法 
  27.                         methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream""println""(Ljava/lang/String;)V"false); 
  28.                         super.onMethodEnter(); 
  29.                     } 
  30.                 }; 
  31.             } 
  32.         }, ClassReader.EXPAND_FRAMES); 
  33.  
  34.         byte[] bytes = classWriter.toByteArray(); 
  35.  
  36.         return (T) new ASMProxy().defineClass(clazz.getName(), bytes, 0, bytes.length).newInstance(); 
  37.     } 
  38.  
  39.  
  40. @Test 
  41. public void test_ASMProxy() throws Exception { 
  42.     IUserApi userApi = ASMProxy.getProxy(UserApi.class); 
  43.     String invoke = userApi.queryUserInfo(); 
  44.     logger.info("測試結(jié)果:{}", invoke); 
  45.  
  46. /** 
  47.  * 測試結(jié)果: 
  48.  *  
  49.  * queryUserInfo 你被代理了,By ASM! 
  50.  * 20:12:26.791 [main] INFO  org.itstack.interview.test.ApiTest - 測試結(jié)果: 沉淀、分享、成長,讓自己和他人都能有所收獲! 
  51.  * 
  52.  * Process finished with exit code 0 
  53.  */ 
  • 場景:全鏈路監(jiān)控、破解工具包、CGLIB、Spring獲取類元數(shù)據(jù)等
  • 點(diǎn)評:這種代理就是使用字節(jié)碼編程的方式進(jìn)行處理,它的實(shí)現(xiàn)方式相對復(fù)雜,而且需要了解Java虛擬機(jī)規(guī)范相關(guān)的知識。因?yàn)槟愕拿恳徊酱聿僮鳎际窃诓僮髯止?jié)碼指令,例如:Opcodes.GETSTATIC、Opcodes.INVOKEVIRTUAL,除了這些還有小200個常用的指令。但這種最接近底層的方式,也是最快的方式。所以在一些使用字節(jié)碼插裝的全鏈路監(jiān)控中,會非常常見。

4. Byte-Buddy代理方式

  1. public class ByteBuddyProxy { 
  2.  
  3.     public static <T> T getProxy(Class clazz) throws Exception { 
  4.  
  5.         DynamicType.Unloaded<?> dynamicType = new ByteBuddy() 
  6.                 .subclass(clazz) 
  7.                 .method(ElementMatchers.<MethodDescription>named("queryUserInfo")) 
  8.                 .intercept(MethodDelegation.to(InvocationHandler.class)) 
  9.                 .make(); 
  10.  
  11.         return (T) dynamicType.load(Thread.currentThread().getContextClassLoader()).getLoaded().newInstance(); 
  12.     } 
  13.  
  14.  
  15. @RuntimeType 
  16. public static Object intercept(@Origin Method method, @AllArguments Object[] args, @SuperCall Callable<?> callable) throws Exception { 
  17.     System.out.println(method.getName() + " 你被代理了,By Byte-Buddy!"); 
  18.     return callable.call(); 
  19.  
  20. @Test 
  21. public void test_ByteBuddyProxy() throws Exception { 
  22.     IUserApi userApi = ByteBuddyProxy.getProxy(UserApi.class); 
  23.     String invoke = userApi.queryUserInfo(); 
  24.     logger.info("測試結(jié)果:{}", invoke); 
  25.  
  26. /** 
  27.  * 測試結(jié)果: 
  28.  *  
  29.  * queryUserInfo 你被代理了,By Byte-Buddy! 
  30.  * 20:19:44.498 [main] INFO  org.itstack.interview.test.ApiTest - 測試結(jié)果: 沉淀、分享、成長,讓自己和他人都能有所收獲! 
  31.  * 
  32.  * Process finished with exit code 0 
  33.  */ 
  • 場景:AOP切面、類代理、組件、監(jiān)控、日志
  • 點(diǎn)評:Byte Buddy 也是一個字節(jié)碼操作的類庫,但 Byte Buddy 的使用方式更加簡單。無需理解字節(jié)碼指令,即可使用簡單的 API 就能很容易操作字節(jié)碼,控制類和方法。比起JDK動態(tài)代理、cglib,Byte Buddy在性能上具有一定的優(yōu)勢。「另外」,2015年10月,Byte Buddy被 Oracle 授予了 Duke's Choice大獎。該獎項(xiàng)對Byte Buddy的“ Java技術(shù)方面的巨大創(chuàng)新 ”表示贊賞。

5. Javassist代理方式

  1. public class JavassistProxy extends ClassLoader { 
  2.  
  3.     public static <T> T getProxy(Class clazz) throws Exception { 
  4.  
  5.         ClassPool pool = ClassPool.getDefault(); 
  6.         // 獲取類 
  7.         CtClass ctClass = pool.get(clazz.getName()); 
  8.         // 獲取方法 
  9.         CtMethod ctMethod = ctClass.getDeclaredMethod("queryUserInfo"); 
  10.         // 方法前加強(qiáng) 
  11.         ctMethod.insertBefore("{System.out.println(\"" + ctMethod.getName() + " 你被代理了,By Javassist\");}"); 
  12.  
  13.         byte[] bytes = ctClass.toBytecode(); 
  14.  
  15.         return (T) new JavassistProxy().defineClass(clazz.getName(), bytes, 0, bytes.length).newInstance(); 
  16.     } 
  17.  
  18.  
  19. @Test 
  20. public void test_JavassistProxy() throws Exception { 
  21.     IUserApi userApi = JavassistProxy.getProxy(UserApi.class
  22.     String invoke = userApi.queryUserInfo(); 
  23.     logger.info("測試結(jié)果:{}", invoke); 
  24.  
  25. /** 
  26.  * 測試結(jié)果: 
  27.  *  
  28.  * queryUserInfo 你被代理了,By Javassist 
  29.  * 20:23:39.139 [main] INFO  org.itstack.interview.test.ApiTest - 測試結(jié)果: 沉淀、分享、成長,讓自己和他人都能有所收獲! 
  30.  * 
  31.  * Process finished with exit code 0 
  32.  */ 

 

  • 場景:全鏈路監(jiān)控、類代理、AOP
  • 點(diǎn)評:Javassist 是一個使用非常廣的字節(jié)碼插裝框架,幾乎一大部分非入侵的全鏈路監(jiān)控都是會選擇使用這個框架。因?yàn)樗幌階SM那樣操作字節(jié)碼導(dǎo)致風(fēng)險,同時它的功能也非常齊全。另外,這個框架即可使用它所提供的方式直接編寫插裝代碼,也可以使用字節(jié)碼指令進(jìn)行控制生成代碼,所以綜合來看也是一個非常不錯的字節(jié)碼框架。

四、總結(jié)

除了JDK、CGLIB,還有3種類代理方式?面試又卡住
  • 代理的實(shí)際目的就是通過一些技術(shù)手段,替換掉原有的實(shí)現(xiàn)類或者給原有的實(shí)現(xiàn)類注入新的字節(jié)碼指令。而這些技術(shù)最終都會用到一些框架應(yīng)用、中間件開發(fā)以及類似非入侵的全鏈路監(jiān)控中。
  • 一個技術(shù)棧深度的學(xué)習(xí)能讓你透徹的了解到一些基本的根本原理,通過這樣的學(xué)習(xí)可以解惑掉一些似懂非懂的疑問,也可以通過這樣技術(shù)的拓展讓自己有更好的工作機(jī)會和薪資待遇。
  • 這些技術(shù)學(xué)起來并不會很容易,甚至可能還有一些燒腦。但每一段值得深入學(xué)習(xí)的技術(shù)都能幫助你突破一定階段的技術(shù)瓶頸。

 

責(zé)任編輯:張燕妮 來源: 今日頭條
相關(guān)推薦

2023-12-06 08:23:44

代理模式設(shè)計(jì)模式

2024-01-04 07:42:44

JavaCGLIBJDK

2022-09-01 10:40:29

SpringAOPJDK

2022-12-02 08:48:16

CSS置灰網(wǎng)站

2015-08-10 14:54:57

公有云云安全數(shù)據(jù)加密

2025-02-27 00:32:35

2024-07-31 11:26:05

反射BeanXML

2021-07-14 11:07:56

AOPJDKCglib

2009-06-15 14:15:07

Java設(shè)計(jì)模式Java

2020-06-17 07:56:19

前端存儲數(shù)據(jù)

2016-10-26 08:57:13

HadoopScrapy大數(shù)據(jù)

2017-01-15 14:18:35

大數(shù)據(jù)HadoopScrapy

2021-08-30 08:40:02

網(wǎng)頁404HTTP

2024-05-23 13:54:40

2011-05-19 15:57:15

測試專家

2021-09-28 09:52:08

Prometheus開源工具Kubernetes

2019-01-15 18:20:57

2012-06-19 09:21:17

BYOD

2022-11-03 08:44:24

代理模式Java設(shè)計(jì)模式

2024-06-12 12:08:03

鏡像倉庫Cloudflare域名
點(diǎn)贊
收藏

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