当前位置: 首页 > 后端技术 > Java

设计模式【3.3】——CGLIB动态代理源码解读

时间:2023-04-01 19:14:29 Java

cglib动态代理cglib介绍CGLIB是一个开源项目,一个强大的高性能高质量代码生成库,可以在运行时扩展Java类,实现Java接口等。底层是使用ASM这种小而快的字节码处理框架,进行字节码转换和生成新的类。理论上我们也可以直接使用ASM直接生成代码,但是需要熟悉JVM内部结构,class文件格式,字节码指令集。JDK包里没有这个东西,需要自己下载导入或者用Maven坐标导入。我选择Maven导入并添加到pom.xml文件:cglibcglib3.3.0Student.java:publicclassStudent{publicvoidlearn(){System.out.println("我是学生,我要学习");}}MyProxy.java(代理类)importnet.sf.cglib.proxy.MethodInterceptor;importnet.sf.cglib.proxy.MethodProxy;importjava.lang.reflect.Method;publicclassStudentProxyimplementsMethodInterceptor{publicObjectinterceptor(Objectobj,Methodmethod,Object[]args,MethodProxyproxy)throwsThrowable{//TODO自动生成的方法存根System.out.println("Beforeproxy--------");proxy.invokeSuper(obj,args);System.out.println("代理后-------");返回空值;}}测试类(Test.java)importnet.sf.cglib.core.DebuggingClassWriter;导入net.sf.cglib.proxy.Enhancer;公共课测试{ppublicstaticvoidmain(String[]args){//代理类文件存放在本地磁盘,方便我们反编译查看源码System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"/Users/aphysia/Desktop");Enhancerenhancer=newEnhancer();enhancer.setSuperclass(Student.class);enhancer.setCallback(newStudentProxy());学生student=(Student)enhancer.create();学生.学习();}}运行后的结果是:CGLIBdebuggingenabled,writingto'/Users/xuwenhao/Desktop'代理前------我是学生,代理后想学习-------在我们选择的文件夹中,生成了代理类代码:源码分析我们要代理的类首先需要实现MethodInterceptor(方法拦截器)接口。该接口只有一个方法拦截,参数为:obj:要增强的对象method:要拦截的方法args:要拦截的方法参数proxy:表示触发父类包net.sf的方法对象.cglib.proxy;importjava.lang.reflect.Method;publicinterfaceMethodInterceptorextendsCallback{publicObjectintercept(Objectobj,java.lang.reflect.Methodmethod,Object[]args,MethodProxyproxy)throwsThrowable;}回顾在enhancer.create()方法中我们要创建一个代理类,这个方法的意思是:如果你需要To,生成一个新类并使用指定的回调(如果有)使用超类的无参数构造函数创建一个新的对象实例。publicObjectcreate(){classOnly=false;参数类型=null;返回createHelper();}主要的方法逻辑我们要看createHelper(),除了校验,就是调用KEY_FACTORY.newInstance()方法生成EnhancerKey对象,KEY_FACTORY是静态EnhancerKey接口,newInstance()是接口中的方法,重点在super.create(key),它调用了父类的方法:privateObjectcreateHelper(){//checkpreValidate();对象键=KEY_FACTORY。newInstance((superclass!=null)?superclass.getName():null,ReflectUtils.getNames(interfaces),filter==ALL_ZERO?null:newWeakCacheKey(filter),callbackTypes,useFactory,interceptDuringConstruction,serialVersionUID);这个.currentKey=键;对象结果=super.create(key);返回结果;}AbstractClassGenerator是Enhancer的父类。create(key)方法的主要逻辑是获取类加载器,缓存并获取类加载数据,然后反映构造对象,其中有两个创建实例对象的方法:fistInstance():该方法不应该在正常流程中调用。从技术上讲,{@link#wrapCachedClass(Class)}使用{@linkEnhancerFactoryData}作为缓存值,这使得实例化比普通的旧反射查找和调用更快。出于向后兼容的原因,此方法保持不变:以防万一它被使用过。(我的理解是目前的逻辑不会走到这个分支,因为比较忙,但是为了兼容,这个case还是省了),内部逻辑其实用的是ReflectUtils.newInstance(type)。nextInstance():真正的创建代理对象的类protectedObjectcreate(Objectkey){try{ClassLoaderloader=getClassLoader();Mapcache=CACHE;ClassLoaderDatadata=cache.get(loader);if(data==null){同步(AbstractClassGenerator.class){cache=CACHE;data=cache.get(loader);if(data==null){MapnewCache=newWeakHashMap(缓存);data=newClassLoaderData(loader);newCache.put(装载机,数据);缓存=新缓存;}}}this.key=key;对象obj=data.get(this,getUseCache());if(objinstanceofClass){returnfirstInstance((Class)对象);}//实际创建对象的方法returnnextInstance(obj);}catch(RuntimeExceptione){抛出e;}catch(错误e){抛出e;}catch(Exceptione){thrownewCodeGenerationException(e);}}该方法定义在AbstractClassGenerator中,但实际上调用的是子类Enhancer的实现,主要是获取参数类型、参数、回调对象,并利用这些参数生成代理对象protectedObjectnextInstance(Objectinstance){EnhancerFactoryData数据=(EnhancerFactoryData)instance;如果(classOnly){返回data.generatedClass;}Class[]argumentTypes=this.argumentTypes;对象[]arguments=this.arguments;if(argumentTypes==null){argumentTypes=Constants.EMPTY_CLASS_ARRAY;参数=空;}//构造returndata.newInstance(argumentTypes,arguments,callbacks);}内部实现总图,调用的都是ReflectUtils.newInstance(),参数类型不一样:publicObjectnewInstance(Class[]argumentTypes,Object[]arguments,Callback[]callbacks){尝试{if(primaryConstructorArgTypes==argumentTypes||Arrays.equals(primaryConstructorArgTypes,argumentTypes)){returnReflectUtils.newInstance(primaryConstructor,arguments);}返回ReflectUtils.newInstance(generatedClass,argumentTypes,arguments);}最后{setThreadCallbacks(null);}}跟进到最后,就是获取构造方法,通过反射构造代理对象,最后调用JDK提供的方法:publicstaticObjectnewInstance(Classtype,Class[]parameterTypes,Object[]args){返回newInstance(getConstructor(type,parameterTypes),args);}publicstaticObjectnewInstance(finalConstructorcstruct,finalObject[]args){booleanflag=cstruct.isAccessible();尝试{如果(!flag){cstruct.setAccessible(true);}对象结果=cstruct.newInstance(args);返回结果;}catch(InstantiationExceptione){thrownewCodeGenerationException(e);catch(IllegalAccessExceptione){thrownewCodeGenerationException(e);}catch(InvocationTargetExceptione){th行新CodeGenerationException(e.getTargetException());}finally{if(!flag){cstruct.setAccessible(flag);}}}打开它自动生成的代理类文件,你会发现确实生成了那些方法,增加了一些增强方法:生成的代理类继承了原来的类:publicclassStudent$$EnhancerByCGLIB$$929cb5feextendsStudentimplementsFactory{...}查看生成的增强方法,其实就是调用了intercept()方法,这个方法是我们自己实现的,所以代理对象增强的功能就完成了:publicfinalvoidlearn(){MethodInterceptorvar10000=this.CGLIB$CALLBACK_0;如果(var10000==null){CGLIB$BIND_CALLBACKS(this);var10000=this.CGLIB$CALLBACK_0;}if(var10000!=null){var10000.intercept(this,CGLIB$learn$0$Method,CGLIB$emptyArgs,CGLIB$learn$0$Proxy);}else{super.learn();cglib和jdk动态代理有什么区别?jdk动态代理使用拦截器和反射生成代理接口的匿名类。执行方法时,交给InvokeHandler处理CGLIB。字节码,然后生成新的子类进行处理。需要JDK代理来实现该接口,但CGLIB不需要。在JDK1.6之前,cglib因为使用了字节码生成技术,所以效率比反射高,但是之后jdk也做了一些优化,效率有所提升。【作者简介】:公众号【秦淮杂货铺】作者秦淮,技术之路不是一时的,山高水长,纵使慢也不会停。个人写作方向:Java源码分析、JDBC、Mybatis、Spring、redis、分布式、简知Offer、LeetCode等,认真写好每篇文章,不喜欢头条党,不喜欢花里胡哨,多写系列的文章,不保证我写的是完全正确的,但我保证我写的是经过实践或搜索过的资料。如有遗漏或错误,敬请指正。剑指提供所有问答PDF2020年我写了什么?开源编程笔记