溫馨提示×

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

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

類(lèi)代理的方式有哪些

發(fā)布時(shí)間:2021-10-26 09:25:15 來(lái)源:億速云 閱讀:149 作者:iii 欄目:web開(kāi)發(fā)

這篇文章主要講解了“類(lèi)代理的方式有哪些”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“類(lèi)代理的方式有哪些”吧!

 五種類(lèi)代理的方式

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

「定義接口」

public interface IUserApi {      String queryUserInfo();  }

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

public class UserApi implements IUserApi {      public String queryUserInfo() {         return "沉淀、分享、成長(zhǎng),讓自己和他人都能有所收獲!";     }  }

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

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

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

1. JDK代理方式

public class JDKProxy {      public static <T> T getProxy(Class clazz) throws Exception {         ClassLoader classLoader = Thread.currentThread().getContextClassLoader();         return (T) Proxy.newProxyInstance(classLoader, new Class[]{clazz}, new InvocationHandler() {             public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {                 System.out.println(method.getName() + " 你被代理了,By JDKProxy!");                 return "沉淀、分享、成長(zhǎng),讓自己和他人都能有所收獲!";             }         });     }  }  @Test public void test_JDKProxy() throws Exception {     IUserApi userApi = JDKProxy.getProxy(IUserApi.class);     String invoke = userApi.queryUserInfo();     logger.info("測(cè)試結(jié)果:{}", invoke); }  /**  * 測(cè)試結(jié)果:  *   * queryUserInfo 你被代理了,By JDKProxy!  * 19:55:47.319 [main] INFO  org.itstack.interview.test.ApiTest - 測(cè)試結(jié)果: 沉淀、分享、成長(zhǎng),讓自己和他人都能有所收獲!  *  * Process finished with exit code 0  */
  • 指數(shù):??

  • 場(chǎng)景:中間件開(kāi)發(fā)、設(shè)計(jì)模式中代理模式和裝飾器模式應(yīng)用

  • 點(diǎn)評(píng):這種JDK自帶的類(lèi)代理方式是非常常用的一種,也是非常簡(jiǎn)單的一種?;緯?huì)在一些中間件代碼里看到例如:數(shù)據(jù)庫(kù)路由組件、Redis組件等,同時(shí)我們也可以使用這樣的方式應(yīng)用到設(shè)計(jì)模式中。

2. CGLIB代理方式

public class CglibProxy implements MethodInterceptor {     public Object newInstall(Object object) {         return Enhancer.create(object.getClass(), this);     }     public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {         System.out.println("我被CglibProxy代理了");         return methodProxy.invokeSuper(o, objects);     } }  @Test public void test_CglibProxy() throws Exception {     CglibProxy cglibProxy = new CglibProxy();     UserApi userApi = (UserApi) cglibProxy.newInstall(new UserApi());     String invoke = userApi.queryUserInfo();     logger.info("測(cè)試結(jié)果:{}", invoke); }  /**  * 測(cè)試結(jié)果:  *   * queryUserInfo 你被代理了,By CglibProxy!  * 19:55:47.319 [main] INFO  org.itstack.interview.test.ApiTest - 測(cè)試結(jié)果:  沉淀、分享、成長(zhǎng),讓自己和他人都能有所收獲!  *  * Process finished with exit code 0  */
  • 場(chǎng)景:Spring、AOP切面、鑒權(quán)服務(wù)、中間件開(kāi)發(fā)、RPC框架等

  • 點(diǎn)評(píng):CGLIB不同于JDK,它的底層使用ASM字節(jié)碼框架在類(lèi)中修改指令碼實(shí)現(xiàn)代理,所以這種代理方式也就不需要像JDK那樣需要接口才能代理。同時(shí)得益于字節(jié)碼框架的使用,所以這種代理方式也會(huì)比使用JDK代理的方式快1.5~2.0倍。

3. ASM代理方式

public class ASMProxy extends ClassLoader {      public static <T> T getProxy(Class clazz) throws Exception {          ClassReader classReader = new ClassReader(clazz.getName());         ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_MAXS);          classReader.accept(new ClassVisitor(ASM5, classWriter) {             @Override             public MethodVisitor visitMethod(int access, final String name, String descriptor, String signature, String[] exceptions) {                  // 方法過(guò)濾                 if (!"queryUserInfo".equals(name))                     return super.visitMethod(access, name, descriptor, signature, exceptions);                  final MethodVisitor methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions);                  return new AdviceAdapter(ASM5, methodVisitor, access, name, descriptor) {                      @Override                     protected void onMethodEnter() {                         // 執(zhí)行指令;獲取靜態(tài)屬性                         methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");                         // 加載常量 load constant                         methodVisitor.visitLdcInsn(name + " 你被代理了,By ASM!");                         // 調(diào)用方法                         methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);                         super.onMethodEnter();                     }                 };             }         }, ClassReader.EXPAND_FRAMES);          byte[] bytes = classWriter.toByteArray();          return (T) new ASMProxy().defineClass(clazz.getName(), bytes, 0, bytes.length).newInstance();     }  }  @Test public void test_ASMProxy() throws Exception {     IUserApi userApi = ASMProxy.getProxy(UserApi.class);     String invoke = userApi.queryUserInfo();     logger.info("測(cè)試結(jié)果:{}", invoke); }  /**  * 測(cè)試結(jié)果:  *   * queryUserInfo 你被代理了,By ASM!  * 20:12:26.791 [main] INFO  org.itstack.interview.test.ApiTest - 測(cè)試結(jié)果: 沉淀、分享、成長(zhǎng),讓自己和他人都能有所收獲!  *  * Process finished with exit code 0  */
  • 場(chǎng)景:全鏈路監(jiān)控、破解工具包、CGLIB、Spring獲取類(lèi)元數(shù)據(jù)等

  • 點(diǎn)評(píng):這種代理就是使用字節(jié)碼編程的方式進(jìn)行處理,它的實(shí)現(xiàn)方式相對(duì)復(fù)雜,而且需要了解Java虛擬機(jī)規(guī)范相關(guān)的知識(shí)。因?yàn)槟愕拿恳徊酱聿僮鳎际窃诓僮髯止?jié)碼指令,例如:Opcodes.GETSTATIC、Opcodes.INVOKEVIRTUAL,除了這些還有小200個(gè)常用的指令。但這種最接近底層的方式,也是最快的方式。所以在一些使用字節(jié)碼插裝的全鏈路監(jiān)控中,會(huì)非常常見(jiàn)。

4. Byte-Buddy代理方式

public class ByteBuddyProxy {      public static <T> T getProxy(Class clazz) throws Exception {          DynamicType.Unloaded<?> dynamicType = new ByteBuddy()                 .subclass(clazz)                 .method(ElementMatchers.<MethodDescription>named("queryUserInfo"))                 .intercept(MethodDelegation.to(InvocationHandler.class))                 .make();          return (T) dynamicType.load(Thread.currentThread().getContextClassLoader()).getLoaded().newInstance();     }  }  @RuntimeType public static Object intercept(@Origin Method method, @AllArguments Object[] args, @SuperCall Callable<?> callable) throws Exception {     System.out.println(method.getName() + " 你被代理了,By Byte-Buddy!");     return callable.call(); }  @Test public void test_ByteBuddyProxy() throws Exception {     IUserApi userApi = ByteBuddyProxy.getProxy(UserApi.class);     String invoke = userApi.queryUserInfo();     logger.info("測(cè)試結(jié)果:{}", invoke); }  /**  * 測(cè)試結(jié)果:  *   * queryUserInfo 你被代理了,By Byte-Buddy!  * 20:19:44.498 [main] INFO  org.itstack.interview.test.ApiTest - 測(cè)試結(jié)果: 沉淀、分享、成長(zhǎng),讓自己和他人都能有所收獲!  *  * Process finished with exit code 0  */
  • 場(chǎng)景:AOP切面、類(lèi)代理、組件、監(jiān)控、日志

  • 點(diǎn)評(píng):Byte Buddy 也是一個(gè)字節(jié)碼操作的類(lèi)庫(kù),但 Byte Buddy 的使用方式更加簡(jiǎn)單。無(wú)需理解字節(jié)碼指令,即可使用簡(jiǎn)單的 API 就能很容易操作字節(jié)碼,控制類(lèi)和方法。比起JDK動(dòng)態(tài)代理、cglib,Byte Buddy在性能上具有一定的優(yōu)勢(shì)。「另外」,2015年10月,Byte Buddy被 Oracle 授予了 Duke's Choice大獎(jiǎng)。該獎(jiǎng)項(xiàng)對(duì)Byte Buddy的“ Java技術(shù)方面的巨大創(chuàng)新 ”表示贊賞。

5. Javassist代理方式

public class JavassistProxy extends ClassLoader {      public static <T> T getProxy(Class clazz) throws Exception {          ClassPool pool = ClassPool.getDefault();         // 獲取類(lèi)         CtClass ctClass = pool.get(clazz.getName());         // 獲取方法         CtMethod ctMethod = ctClass.getDeclaredMethod("queryUserInfo");         // 方法前加強(qiáng)         ctMethod.insertBefore("{System.out.println(\"" + ctMethod.getName() + " 你被代理了,By Javassist\");}");          byte[] bytes = ctClass.toBytecode();          return (T) new JavassistProxy().defineClass(clazz.getName(), bytes, 0, bytes.length).newInstance();     }  }  @Test public void test_JavassistProxy() throws Exception {     IUserApi userApi = JavassistProxy.getProxy(UserApi.class)     String invoke = userApi.queryUserInfo();     logger.info("測(cè)試結(jié)果:{}", invoke); }  /**  * 測(cè)試結(jié)果:  *   * queryUserInfo 你被代理了,By Javassist  * 20:23:39.139 [main] INFO  org.itstack.interview.test.ApiTest - 測(cè)試結(jié)果: 沉淀、分享、成長(zhǎng),讓自己和他人都能有所收獲!  *  * Process finished with exit code 0  */
  • 場(chǎng)景:全鏈路監(jiān)控、類(lèi)代理、AOP

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

感謝各位的閱讀,以上就是“類(lèi)代理的方式有哪些”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)類(lèi)代理的方式有哪些這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

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

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

AI