网站地图什么意思,沈阳德泰诺网站建设,网页设计页面设计主要技术,昆山网站建设详细方案如有错误欢迎指出
是jdk动态代理是从一步步开始写完特性/维度JDK 动态代理CGLIB 动态代理是否需要接口✅ 需要接口❌ 不需要接口#xff08;可代理普通类#xff09;代理原理基于反射实现接口方法的代理继承目标类并重写方法#xff0c;基于 ASM 字节码操作代理类结构生成实…如有错误欢迎指出是jdk动态代理是从一步步开始写完特性/维度JDK 动态代理CGLIB 动态代理是否需要接口✅ 需要接口❌ 不需要接口可代理普通类代理原理基于反射实现接口方法的代理继承目标类并重写方法基于 ASM 字节码操作代理类结构生成实现目标接口的代理类生成子类目标类的子类性能启动时创建代理类较快创建代理类稍慢涉及字节码操作性能执行时方法调用稍慢反射方法调用较快直接调用子类方法适合场景有接口的服务类如 Service 接口层没有接口的类或对性能要求较高的场景常用框架支持Spring AOP 默认使用 JDK Proxy有接口时Spring AOP 在无接口时使用 CGLIB类或方法限制接口方法都能代理final 类或 final 方法不能被代理生成方式Proxy.newProxyInstance()Enhancer.create()来自 CGLIB配置示例Springaop:aspectj-autoproxy/默认配合aop:aspectj-autoproxy proxy-target-classtrue/https://www.doubao.com/thread/w25860ff971baf089java proxy增强publicclassJdkProxyDemo{interfaceFoo{voidfoo();intbar();}staticfinalclassTargetimplementsFoo{publicvoidfoo(){System.out.println(target foo);}publicintbar(){System.out.println(target bar);return100;}}// jdk 只能针对接口代理// cglibpublicstaticvoidmain(String[]param)throwsIOException{// 目标对象TargettargetnewTarget();///获取类加载器ClassLoaderloaderJdkProxyDemo.class.getClassLoader();// 创建代理对象Fooproxy(Foo)Proxy.newProxyInstance(loader,// 类加载器newClass[]{Foo.class},// 代理类需要实现的接口newInvocationHandler(){// 方法调用处理器OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{System.out.println(before...);// 方法调用前的增强逻辑Objectresultmethod.invoke(target,args);// 调用目标对象的方法System.out.println(after....);// 方法调用后的增强逻辑returnresult;// 返回目标方法的执行结果}});proxy.foo();proxy.bar();}}before... target foo after.... before... target bar after....publicstaticObjectnewProxyInstance(ClassLoaderloader,//加载器Class?[]interfaces,///实现的接口InvocationHandlerh)throwsIllegalArgumentException{}publicinterfaceInvocationHandler{publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)//代理逻辑throwsThrowable;}java cglib Enhancer代理增强继承父类实现importnet.sf.cglib.proxy.Enhancer;importnet.sf.cglib.proxy.MethodInterceptor;importnet.sf.cglib.proxy.MethodProxy;importjava.lang.reflect.Method;publicclassCglibProxyDemo{staticclassTarget{publicvoidfoo(){System.out.println(target foo too);}}publicstaticvoidmain(String[]args){// 使用 CGLIB 创建代理对象Targetproxy(Target)Enhancer.create(Target.class,newMethodInterceptor(){OverridepublicObjectintercept(Objectproxy,Methodmethod,Object[]args,MethodProxymethodProxy)throwsThrowable{System.out.println(before...);// 方式 1反射调用目标对象方法性能较差// Object result method.invoke(new Target(), args);// 方式 2使用 methodProxy.invokeSuper() 调用父类方法CGLIB 方式性能更好ObjectresultmethodProxy.invokeSuper(proxy,args);System.out.println(after...);returnresult;}});// 调用代理方法proxy.foo();}}jdk 反射源码探究第一个版本代理类publicclassA01proxy{interfaceFoo{voidfoo();}publicstaticclassTargetimplementsFoo{Overridepublicvoidfoo(){System.out.println(Target.foo());}}publicstaticvoidmain(String[]args){FooProxynewProxyq();Proxy.foo();}}publicclassProxyqimplementsA01proxy.Foo{Overridepublicvoidfoo(){System.out.println(foo);newTarget().foo();}}fooTarget.foo()Target.foo()]()//类似于什么设计模式呢缺点-代理的方法直接写死到内部如果要新增很麻烦版本2优化点解耦 -让代理的方法交给外部来写新增接口-使得逻辑再外部使用时实现接口publicinterfaceInvocationHandler{voidinvoke();}publicclassProxyqimplementsA01proxy.Foo{privateInvocationHandlerh;publicProxyq(com.itheima.a13.a02.InvocationHandlerfoo){this.hfoo;}Overridepublicvoidfoo(){h.invoke();}}publicclassA01proxy{interfaceFoo{voidfoo();}publicstaticclassTargetimplementsFoo{Overridepublicvoidfoo(){System.out.println(Target.foo());}}publicstaticvoidmain(String[]args){//外部实现FooProxynewProxyq(newInvocationHandler(){Overridepublicvoidinvoke(){System.out.println(foo);newTarget().foo();}});Proxy.foo();}}缺点如果是boo方法想代理呢那么就会出现错误。(Method method需要传递调用的方法来动态调用Proxy.boo()不知道怎么处理。三 优化方法过多无法处理publicinterfaceInvocationHandler{voidinvoke(Methodmethod,Object[]args)throwsInvocationTargetException,IllegalAccessException;}我们从源码中看到就是这样我们来实现一下publicclassA01proxy{interfaceFoo{voidfoo()throwsNoSuchMethodException,InvocationTargetException,IllegalAccessException;voidbar()throwsNoSuchMethodException,InvocationTargetException,IllegalAccessException;}publicstaticclassTargetimplementsFoo{Overridepublicvoidfoo(){System.out.println(Target.foo());}Overridepublicvoidbar(){System.out.println(Target.bar());}}publicstaticvoidmain(String[]args)throwsInvocationTargetException,NoSuchMethodException,IllegalAccessException{FooProxynewProxyq(newInvocationHandler(){Overridepublicvoidinvoke(Methodmethod,Object[]args)throwsInvocationTargetException,IllegalAccessException{System.out.println(before);method.invoke(newTarget(),args);///反射}});Proxy.foo();Proxy.bar();}}publicinterfaceInvocationHandler{voidinvoke(Methodmethod,Object[]args)throwsInvocationTargetException,IllegalAccessException;}publicclassProxyqimplementsA01proxy.Foo{privateInvocationHandlerh;publicProxyq(InvocationHandlerfoo){this.ht;}Overridepublicvoidfoo()throwsNoSuchMethodException,InvocationTargetException,IllegalAccessException{System.out.println(Proxyq.foo());MethodfooA01proxy.Foo.class.getMethod(foo);//反射h.invoke(foo,newObject[0]);//执行}Overridepublicvoidbar()throwsNoSuchMethodException,InvocationTargetException,IllegalAccessException{System.out.println(Proxyq.bar());MethodbarA01proxy.Foo.class.getMethod(bar);h.invoke(bar,newObject[0]);}}反射拿去了方法缺点拿不到返回值优化 3.1返回值处理publicstaticvoidmain(String[]args)throwsInvocationTargetException,NoSuchMethodException,IllegalAccessException{FooProxynewProxyq(newInvocationHandler(){OverridepublicObjectinvoke(Methodmethod,Object[]args)throwsInvocationTargetException,IllegalAccessException{//返回值处理System.out.println(before);returnmethod.invoke(newTarget(),args);}});Proxy.foo();Proxy.bar();}publicinterfaceInvocationHandler{Objectinvoke(Methodmethod,Object[]args)throwsInvocationTargetException,IllegalAccessException;}Overridepublicvoidfoo()throwsNoSuchMethodException,InvocationTargetException,IllegalAccessException{System.out.println(Proxyq.foo());MethodfooA01proxy.Foo.class.getMethod(foo);Objectresulth.invoke(foo,newObject[0]);//这里需要return但是我没有处理}优化 保存与jdk一致新增参数object-吧代理类传到实现里面去了异常处理注意异常顺序代码优化///防止多次反射拿去method最终publicclass$Proxy0implementstest01.Foo{//代理类test01.InvocationHandlerh;public$Proxy0(test01.InvocationHandlerh){this.hh;}staticMethodfoo;staticMethodbar;static{try{footest01.Foo.class.getMethod(foo);bartest01.Foo.class.getMethod(bar);}catch(NoSuchMethodExceptione){thrownewNoSuchMethodError(e.getMessage());}}//代理方法Overridepublicvoidfoo(){try{h.invoke(this,foo,newObject[0]);}catch(RuntimeException|Errore){throwe;}catch(Throwablee){thrownewUndeclaredThrowableException(e);}}Overridepublicintbar(){try{Objectresulth.invoke(this,bar,newObject[0]);return(int)result;}catch(RuntimeException|Errore){throwe;}catch(Throwablee){thrownewUndeclaredThrowableException(e);}}}publicclasstest01{interfaceFoo{voidfoo();intbar();}staticclassTargetimplementsFoo{publicvoidfoo(){System.out.println(target foo);}Overridepublicintbar(){System.out.println(target bar);return100;}}interfaceInvocationHandler{Objectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable;}publicstaticvoidmain(String[]param){Fooproxynew$Proxy0(newInvocationHandler(){OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{// 1. 功能增强System.out.println(before...);//2.调用方法Objectresultmethod.invoke(newTarget(),args);returnresult;}});proxy.foo();proxy.bar();}}真实源码由编译器生成的吗还是java语法特性JVM 在运行期生成$Proxy类运行期publicclassA12{interfaceFoo{voidfoo();intbar();}staticclassTargetimplementsFoo{publicvoidfoo(){System.out.println(target foo);}Overridepublicintbar(){System.out.println(target bar);return100;}}/*interface InvocationHandler { Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }*/publicstaticvoidmain(String[]param)throwsIOException{Fooproxynew$Proxy0(newInvocationHandler(){OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{// 1. 功能增强System.out.println(before...);// 2. 调用目标// new Target().foo();returnmethod.invoke(newTarget(),args);}});System.out.println(proxy.getClass().getName());proxy.foo();proxy.bar();System.in.read();/* 学到了什么: 代理一点都不难, 无非就是利用了多态、反射的知识 1. 方法重写可以增强逻辑, 只不过这【增强逻辑】千变万化, 不能写死在代理内部 2. 通过接口回调将【增强逻辑】置于代理类之外 3. 配合接口方法反射(也是多态), 就可以再联动调用目标方法 */}}jdk代理字节码生成asm技术-动态生成字节码文件jdk反射优化反射调用次数过程先用 Native 实现应对低频调用。检测到高频调用后再生成字节码类GeneratedMethodAccessor让反射调用性能接近普通方法调用。访问器类型表示 JVM 在不同阶段使用的MethodAccessor实现。红圈标的TestMethodInvoke.foo((int)c)说明 JVM 已经把反射“编译”成了直接调用。这种生成类只在高频调用的情况下才会出现目的是避免一开始就生成大量类浪费内存。cglib原理介绍CGLIBCode Generation Library是一个基于 ASM 的字节码生成库用来在运行期生成类。它可以在运行期为某个类创建一个子类代理从而实现方法拦截AOP。JDK 动态代理只能代理接口。Spring 的很多 bean 没有实现接口所以需要使用CGLIB生成一个子类去做代理。例如classUserService{...}↓classUserService$$EnhancerByCGLIBextendsUserService{...}然后把你原来对象的功能“增强”。CGLIB 的核心构成只有两点继承目标类生成子类CGLIB 通过 ASM 动态生成一个子类子类中重写目标类的非 final 方法在重写方法中调用拦截器MethodInterceptorMethodInterceptor.intercept() 做方法增强代理类方法 → MethodInterceptor.intercept()你可以在 intercept 里加日志加权限校验加事务加耗时统计然后决定是否调用父类原方法proxy.invokeSuper不调用原方法拦截修改参数修改返回值CGLIB 的调用链非常关键假设目标类classService{voidsave(){...}}CGLIB 会生成classService$$EnhancerByCGLIBextendsService{Overridevoidsave(){// 调用拦截器interceptor.intercept(this,saveMethod,args,methodProxy);}}serviceProxy.save()↓intercept(proxyObj,method,args,methodProxy)↓ methodProxy.invokeSuper(proxyObj,args)↓ 父类Service.save()MethodProxy 的作用面试常问CGLIB 为每个方法创建一个MethodProxy它负责 绑定代理类方法与父类方法的映射 在调用父类方法时使用FastClass避免反射 提供 invokeSuper 方法跳过代理逻辑method.invoke()→ 反射 → 慢 methodProxy.invokeSuper()→FastClass→ 快FastClass 原理提高性能的关键CGLIB 会为类生成一个 “FastClass”把方法调用变成整数索引methodIndex3switch(3):case3:returnobj.save()避免使用反射性能大幅提升。JDK 动态代理无法达到这个性能。EnhancerenhancernewEnhancer();enhancer.setSuperclass(Target.class);enhancer.setCallback(newMethodInterceptor(){OverridepublicObjectintercept(Objectobj,Methodm,Object[]args,MethodProxyproxy)throwsThrowable{System.out.println(before...);Objectresultproxy.invokeSuper(obj,args);System.out.println(after...);returnresult;}});Targetproxy(Target)enhancer.create();proxy.save();CGLIB 运行期生成子类 重写方法 拦截调用 FastClass 加速代码有字段没问题CGLIB 会继承它们。构造函数不执行字段初始化可能不完整。final/private/static 方法无法被增强。如果字段依赖构造注入可能出现 null。继承方法步骤操作代码示例说明1获取Class对象Class? clazz Target.class;反射的入口拿到类的元数据。也可以用Class.forName(包名.Target)。2获取Method对象Method m clazz.getMethod(save);通过方法名和参数类型获取public方法。若方法是私有的或非public用getDeclaredMethod。3创建目标对象实例Target obj new Target();如果要调用实例方法需要先有对象。静态方法可以直接传null调用。4调用方法m.invoke(obj);invoke第一个参数是目标对象后面是方法参数。5处理异常NoSuchMethodException、IllegalAccessException、InvocationTargetException反射涉及多种检查型异常需要捕获或抛出。初始化了 反射拿取了方法-同时创建了一个能够在运行期执行“原始方法/父类方法” 的快速调用器invocation helper。save0ProxyMethodProxy.create(Target.class,// 被代理的类Proxy.class,// 代理类由 CGLIB 生成的子类()V,// 方法签名这里是无参、void 返回值save,// 原方法名saveSuper// 父类方法名或调用原始实现的方法名);步骤反射 (Method.invoke) 做法CGLIB (MethodProxy.invoke) 做法速度差异原因1. 定位方法运行时查找Method对象Class.getMethodMethodProxy.create(...)时直接生成代理类字节码方法引用是编译进字节码的反射是运行时查找CGLIB 是类加载时就确定好2. 访问权限检查每次调用都会做Method的访问修饰符检查JVM 安全检查CGLIB 生成的子类直接调用super.save()不触发反射的安全检查避免了重复安全检查3. 参数传递invoke内部用 Object 数组拆装参数装箱/拆箱CGLIB 生成的方法直接用原始参数类型传递例如直接void save()调用避免了装箱拆箱和类型转换4. 方法调用JVM 通过反射调用MethodAccessor的invoke方法间接层多CGLIB 代理类直接发起super.save()的字节码调用少了中间调用层直接走 invokevirtual 或 invokespecial5. 调用父类实现需要反射再找父类的Method已在create时绑定saveSuper方法生成字节码直接调用直接调用字节码方法引用最终效果每次调用都走反射 API动态解析调用时和普通方法几乎一样快接近直接调用速度大幅减少运行期开销create(...)这一步CGLIB 就会用 ASM 动态生成一个Proxy.class子类并且在字节码里写死类似publicvoidsave(){// 先执行增强逻辑...super.save();// 直接字节码调用}这样后续invoke调用的时候就直接跳到字节码方法完全绕过了Method.invoke的反射路径。后续只需调用即可Objectintercept(Objectobj,Methodmethod,Object[]args,MethodProxyproxy)throwsThrowable{// 增强逻辑前置如日志、权限校验System.out.println(方法调用前增强);// 通过 proxy 调用父类原始方法Objectresultproxy.invokeSuper(obj,args);// 增强逻辑后置如事务提交、日志记录System.out.println(方法调用后增强);returnresult;}// CGLIB 动态生成的代理类里invokeSuper 实际上是跳过代理直接调用父类方法publicObjectinvokeSuper(Objectobj,Object[]args)throwsThrowable{// obj 是代理对象调用父类Target的 save 方法传入参数 args// 这里调用的是类似 JVM 指令 invokespecial直接调用父类实现避免代理递归returninvokeParentMethod(obj,save,args);}intercept 是代理切入点invokeSuper 是跳过代理类直接调用父类方法的快速路径CGLIB 使用 FastClass 避免反射提高性能invokeSuper 用 invokespecial 指令调用父类实现防止递归走两个 一个反射调用 一个代理类调用before...save()before...save(int)before...save(long)cglib 无反射原理代理类调用避免反射重写该fastclass.class- invoke 方法fastclasspublicclassTargetFastClass{//创建方法签名staticSignatures0newSignature(save,()V);staticSignatures1newSignature(save,(I)V);staticSignatures2newSignature(save,(J)V);// 获取目标方法的编号/* Target save() 0 save(int) 1 save(long) 2 signature 包括方法名字、参数返回值 */publicintgetIndex(Signaturesignature){if(s0.equals(signature)){return0;}elseif(s1.equals(signature)){return1;}elseif(s2.equals(signature)){return2;}return-1;}// 根据方法编号, 正常调用目标对象方法publicObjectinvoke(intindex,Objecttarget,Object[]args){if(index0){((Target)target).save();returnnull;}elseif(index1){((Target)target).save((int)args[0]);returnnull;}elseif(index2){((Target)target).save((long)args[0]);returnnull;}else{thrownewRuntimeException(无此方法);}}publicstaticvoidmain(String[]args){TargetFastClassfastClassnewTargetFastClass();intindexfastClass.getIndex(newSignature(save,(I)V));System.out.println(index);fastClass.invoke(index,newTarget(),newObject[]{100});}}代理方法 继承了原始类publicclassProxyFastClass{staticSignatures0newSignature(saveSuper,()V);staticSignatures1newSignature(saveSuper,(I)V);staticSignatures2newSignature(saveSuper,(J)V);// 获取代理方法的编号/* Proxy saveSuper() 0 saveSuper(int) 1 saveSuper(long) 2 signature 包括方法名字、参数返回值 */publicintgetIndex(Signaturesignature){if(s0.equals(signature)){return0;}elseif(s1.equals(signature)){return1;}elseif(s2.equals(signature)){return2;}return-1;}// 根据方法编号, 正常调用目标对象方法publicObjectinvokeSuper(intindex,Objectproxy,Object[]args){if(index0){((Proxy)proxy).saveSuper();returnnull;}elseif(index1){((Proxy)proxy).saveSuper((int)args[0]);returnnull;}elseif(index2){((Proxy)proxy).saveSuper((long)args[0]);returnnull;}else{thrownewRuntimeException(无此方法);}}publicstaticvoidmain(String[]args){ProxyFastClassfastClassnewProxyFastClass();intindexfastClass.getIndex(newSignature(saveSuper,()V));System.out.println(index);fastClass.invokeSuper(index,newProxy(),newObject[0]);}}区别一个调用16次后进行一个方法一个代理的避免反射一个就是两个代理类避免反射 -代理类中的invoke是调用了原始方法-继承写的