和五种代理方法。毫不奇怪,您可能只知道两种类型的代理方法。一个是JDK自带的,一个是CGLIB。我们先定义一个接口和对应的实现类,方便后续使用代理类在方法中添加输出信息。《定义接口》publicinterfaceIUserApi{StringqueryUserInfo();}《实现接口》publicclassUserApiimplementsIUserApi{publicStringqueryUserInfo(){return《沉淀、分享、成长,让自己和他人有所收获!》;}}好!接下来,我们将使用代理向此类方法添加一行额外的输出信息。0.先补充一点反射知识out.println(invoke);}点评:有代理的地方,几乎都会有反射。它们是一组相互结合使用的功能类。在反射中可以调用方法、获取属性、获取注解等相关内容。这些可以与以下类代理结合使用,以完成各种框架中的技术场景。1、JDK代理方法publicclassJDKProxy{publicstaticTgetProxy(Classclazz)throwsException{ClassLoaderclassLoader=Thread.currentThread().getContextClassLoader();return(T)Proxy.newProxyInstance(classLoader,newClass[]{clazz},newInvocationHandler(){publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{System.out.println(method.getName()+"你被代理了,ByJDKProxy!");return"沉淀,分享,成长,让自己和他人能够一些收益!”;}});}}@Testpublicvoidtest_JDKProxy()throwsException{IUserApiuserApi=JDKProxy.getProxy(IUserApi.class);Stringinvoke=userApi.queryUserInfo();logger.info("测试结果:{}",invoke);}/***测试结果:**queryUserInfo你被代理了,ByJDKProxy!*19:55:47.319[main]INFOorg.itstack.interview.test.ApiTest-测试结果:沉淀、分享、成长,让自己和他人都有所收获!**Processfinishedwithexitcode0*/索引:??场景:中间件开发,代理模式和装饰器模式在设计模式中的应用点评:JDK自带的这个类代理方法,是一个很普通也很简单的方法。基本上,你会在一些中间件代码中看到它,比如:数据库路由组件、Redis组件等,我们也可以将这种方法应用到设计模式中。2.CGLIB代理方法publicclassCglibProxyimplementsMethodInterceptor{publicObjectnewInstall(Objectobject){returnEnhancer.create(object.getClass(),this);}publicObjectintercept(Objecto,Methodmethod,Object[]objects,MethodProxymethodProxy)throwsThrowable{System.out.println("我是CglibProxy代理");returnmethodProxy.invokeSuper(o,objects);}}@Testpublicvoidtest_CglibProxy()throwsException{CglibProxycglibProxy=newCglibProxy();UserApiuserApi=(UserApi)cglibProxy.newInstall(newUserApi());Stringinvoke=userApi.query)UserInfo(;logger.info("Testresult:{}",invoke);}/***测试结果:**queryUserInfo你被代理了,ByCglibProxy!*19:55:47.319[main]INFOorg.itstack.interview.test.ApiTest-测试结果:沉淀,分享,成长,让自己和他人都有所收获!**Processfinishedwithexitcode0*/场景:Spring,AOP切面,认证服务,中间件开发,RPC框架等,这种代理方式不需要接口即可proxylikeJDK,同时由于使用了字节码框架,这种代理方式会比JDK代理方式快1.5~2.0倍。3.ASM代理方法publicclassASMProxyextendsClassLoader{publicstaticTgetProxy(Classclazz)throwsException{ClassReaderclassReader=newClassReader(clazz.getName());ClassWriterclassWriter=newClassWriter(classReader,ClassWriter.COMPUTE_MAXS);classReader.accept(newClassVisitor(ASM5,classWriter){@OverridepublicMethodVisitorvisitMethod(intaccess,finalStringname,Stringdescriptor,Stringsignature,String[]exceptions){//方法过滤super.visitMethod(access,name,descriptor,signature,exceptions);returnnewAdviceAdapter(ASM5,methodVisitor,access,name,descriptor){@OverrideprotectedvoidonMethodEnter(){//执行指令;获取静态属性methodVisitor.visitFieldInsn(Opcodes.GETSTATIC,"java/lang/System","out","Ljava/io/PrintStream;");//加载常量loadconstantmethodVisitor.visitLdcInsn(name+"Youareproxied,ByASM!");//调用方法methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL,"java/io/PrintStream","println","(Ljava/lang/String;)V",false);super.onMethodEnter();}};}},ClassReader.EXPAND_FRAMES);byte[]bytes=classWriter.toByteArray();return(T)newASMProxy().defineClass(clazz.getName(),bytes,0,bytes.length).newInstance();}}@Testpublicvoidtest_ASMProxy()throwsException{IUserApiuserApi=ASMProxy.getProxy(UserApi.class);Stringinvoke=userApi.queryUserInfo();logger.info("Testresult:{}",invoke);}/***测试结果:**queryUserInfo你被代理了,By阿斯玛!*20:12:26.791[main]INFOorg.itstack.interview.test.ApiTest-测试结果:沉淀、分享、成长,让自己和他人都有所收获!**Processfinishedwithexitcode0*/场景:全链路监控、破解工具包、CGLIB、Spring获取类元数据等点评:这种代理是字节码编程处理的,实现比较复杂,需要了解相关知识与Java虚拟机规范相关因为你代理操作的每一步都在操作字节码指令,比如:Opcodes.GETSTATIC,Opcodes.INVOKEVIRTUAL,除了这些,还有不到200条常用的指令。但是这种最接近底层的方式,也是最快的方式。所以在一些使用字节码插桩的全链路监控中非常常见。4.字节好友代理方法publicclassByteBuddyProxy{publicstaticTgetProxy(Classclazz)throwsException{DynamicType.Unloaded>dynamicType=newByteBuddy().subclass(clazz).method(ElementMatchers.named("queryUserInfo")).intercept(MethodDelegation.to(InvocationHandler.class)).make();return(T)dynamicType.load(Thread.currentThread().getContextClassLoader()).getLoaded().newInstance();}}@RuntimeTypepublicstaticObjectintercept(@OriginMethodmethod,@AllArgumentsObject[]args,@SuperCallCallable>callable)throwsException{System.out.println(method.getName()+"你被委托了,ByByte-Buddy!");returncallable.call();}@Testpublicvoidtest_ByteBuddyProxy()throwsException{IUserApiuserApi=ByteBuddyProxy.getProxy(UserApi.class);Stringinvoke=userApi.queryUserInfo();logger.info("测试结果:{}",invoke);}/***测试结果:**queryUserInfo代表你,ByByte-Buddy!*20:19:44.498[main]INFOorg.itstack.interview.test.ApiTest-测试结果:沉淀、分享、成长,让自己和他人有所收获报酬!**Processfinishedwithexitcode0*/场景:AOP切面、类代理、组件、监控、日志点评:ByteBuddy也是一个字节码操作的类库,但是ByteBuddy在不了解字节码指令的情况下更容易上手简单的API使得操作字节码变得简单,控制类和方法。与JDK动态代理和cglib相比,字节好友在性能上有一定的优势。“此外”,2015年10月,字节好友被甲骨文授予杜克之选奖。该奖项旨在表彰ByteBuddy“在Java技术方面的巨大创新”。5.Javassist代理方法publicclassJavasistProxyextendsClassLoader{publicstaticTgetProxy(Classclazz)throwsException{ClassPoolpool=ClassPool.getDefault();//获取类CtClassctClass=pool.get(clazz.getName());//获取方法CtMethodctMethod=类。getDeclaredMethod("queryUserInfo");//方法前加强ctMethod.insertBefore("{System.out.println(\""+ctMethod.getName()+"你被代理了,ByJavassist\");}");byte[]bytes=ctClass.toBytecode();返回(T)newJavasistProxy().defineClass(clazz.getName(),bytes,0,bytes.length).newInstance();}}@Testpublicvoidtest_JavassistProxy()throwsException{IUserApiuserApi=JavasistProxy.getProxy(UserApi.class)Stringinvoke=userApi.queryUserInfo();logger.info("Testresult:{}",invoke);}/***Testresult:**queryUserInfoyouareproxied,ByJavassist*20:23:39.139[main]INFOorg.itstack.interview.test.ApiTest-测试结果:沉淀、分享、成长,让自己和他人都有所收获!**Processfinishedwithexitcode0*/场景:全链路监控,类代理,AOP点评:Javassist是一个使用非常广泛的字节码检测框架,几乎大部分的非侵入式全链路监控都会选择使用这个框架。因为它不想像ASM那样去操作字节码带来风险,而且它的功能也很齐全。另外,这个框架可以使用它提供的方法直接编写检测代码,也可以使用字节码指令来控制和生成代码,所以整体来说也是一个非常不错的字节码框架。4.小结代理的实际目的是通过一些技术手段来替换原来的实现类或者在原来的实现类中注入新的字节码指令。而这些技术最终会用在一些框架应用、中间件开发、无创全链路监控等方面。深入研究一个技术栈可以让你彻底理解一些基本的基本原理。通过这种学习,可以解决一些一知半解的问题,也可以通过这项技术的拓展,获得更好的工作机会。和薪资待遇。这些技术都不会好学,甚至可能有点烧脑。但是每一项值得深入研究的技术,都可以帮助你在某个阶段突破技术瓶颈。