字节码编程丨使用Javassist生成JavaBean
这种方式几乎不需要修改源程序就能够达到我们想要的效果。今天,我们就一起使用Javassist来动态生成JavaBean对象。
掌握这个知识点后以便后续我们在手撸DAPM(分布式性能管理系统)时能够动态生成JavaBean对象来反序列化客户端发送的数据,或者从服务端响应回来的数据。开发环境JDK1。8IDEA2018。03Maven3。6。0Maven依赖
在项目的pom。xml文件中添加如下环境依赖。propertiesjavassist。version3。20。0GAjavassist。versionpropertiesdependenciesdependencygroupIdorg。javassistgroupIdjavassistartifactIdversion{javassist。version}versiondependencydependencies案例效果
整体案例的效果比较简单,就是通过运行我们写的程序,能够动态生成User类的class字节码。如下所示。packageio。binghe。bytecode。javassist。bean;publicclassUser{privateStringnamebinghe;publicUser(){this。namebinghe;}publicUser(Stringvar1){this。namevar1;}publicvoidsetName(Stringvar1){this。namevar1;}publicStringgetName(){returnthis。name;}publicvoidprintName(){System。out。println(this。name);}}在这个User类中,有一个成员变量name,默认值为binghe。分别有一个无参构造方法和有参构造方法。成员变量name的getset方法。打印成员变量name的方法printName()。
了解完案例的效果后,我们就开始动手实现如何动态生成这个User类。案例实现
具体的案例实现,我们可以参考案例的效果一步步完成,这里,我们可以将整个User类的动态生成过程分为6个步骤,分别为:创建User类。添加name字段。添加无参构造方法。添加有参构造方法。添加getset方法。添加printName()方法。
好了,说干就干,接下来就按照这5个步骤动态生成User类。创建User类使用默认的ClassPoolClassPoolpoolClassPool。getDefault();1。创建一个空类CtClassctClasspool。makeClass(io。binghe。bytecode。javassist。bean。User);
User类的创建方法和我们之前创建HelloWorld的类是相同的,首先是获取一个ClassPool对象,通过调用ClassPool对象的makeClass方法创建User类。添加name字段2。新增一个字段privateStringname;字段的名称为nameCtFieldparamnewCtField(pool。get(java。lang。String),name,ctClass);设置访问修饰符为privateparam。setModifiers(Modifier。PRIVATE);设置字段的初始值为binghectClass。addField(param,CtField。Initializer。constant(binghe));
为User类添加成员变量name时,使用了Javassist中的CtField类。这里,我们使用的CtField的构造方法的第一个参数是成员变量的类型,第二个参数是变量的名称,第三个字段表示将这个变量添加到哪个类。
创建完CtField对象param后,我们调用了param的setModifiers()方法设置访问修饰符,这里将其设置为private。
接下来,为成员变量name赋默认值binghe。上述代码生成的效果如下所示。privateStringnamebinghe;添加无参构造方法3。添加无参的构造函数CtConstructorconstructornewCtConstructor(newCtClass〔〕{},ctClass);constructor。setBody({0。namebinghe;});ctClass。addConstructor(constructor);
添加无参构造方法时,使用了Javassist中的CtConstructor类,第一个参数是动态生成的目标类的构造方法的参数类型数组,第二个参数表示将构造方法添加到哪个类中。
接下来,通过调用CtConstructor的setBody()方法设置无参构造方法的方法体。这里需要注意的是方法体中只有一行代码时,可以省略{},但是为了防止出错,冰河强烈建议无论方法是否只有一行代码,都不要省略{}。
细心的小伙伴肯定会发现在方法体中通过0引用了成员变量name,估计小伙伴们也猜到了这个0是干啥的。没错,它在生成User类后会被编译成this。
在Javassist中,还会有一些其他具有特定含义的符号,这个我们在文章的最后统一说明。
这段代码的效果如下所示。publicUser(){this。namebinghe;}
接下来,就是调用CtClass的addConstructor()方法为User类添加无参构造方法。添加有参构造方法4。添加有参构造函数constructornewCtConstructor(newCtClass〔〕{pool。get(java。lang。String)},ctClass);constructor。setBody({0。name1;});ctClass。addConstructor(constructor);
添加有参构造方法的整体流程和添加无参构造方法的整体流程相同,只是在创建CtConstructor对象时,在CtConstructor的构造方法的第一个参数类型数组中使用pool。get(java。lang。String)添加了一个数组元素,表示生成的目标类的构造方法存在一个String类型的参数。
另外,在设置方法体时,使用了如下代码。0。name1;
表示将构造方法的第一个参数赋值给成员变量name。这里,0表示this,1表示第一个参数,2表示第二个参数,以此类推。
这段代码的效果如下所示。publicUser(Stringvar1){this。namevar1;}添加getset方法5。添加getter和setter方法ctClass。addMethod(CtNewMethod。setter(setName,param));ctClass。addMethod(CtNewMethod。getter(getName,param));
添加getset方法就比较简单了,直接使用CtClass的addMethod()添加,使用CtNewMethod的setter()方法生成set方法,其中,第一个参数为生成的方法的名称setName,第二个参数表示是为哪个字段生成setName方法。
使用CtNewMethod的getter()方法生成get()方法,第一个参数为生成的方法的名称getName,第二个参数表示是为哪个字段生成getName方法。
这段代码的效果如下所示。publicvoidsetName(Stringvar1){this。namevar1;}publicStringgetName(){returnthis。name;}添加printName()方法6。创建一个输出name的方法CtMethodctMethodnewCtMethod(CtClass。voidType,printName,newCtClass〔〕{},ctClass);ctMethod。setModifiers(Modifier。PUBLIC);ctMethod。setBody({System。out。println(name);});ctClass。addMethod(ctMethod);
添加printName()方法使用了Javassist中的CtMethod类,创建CtMethod类的对象时,第一个参数为方法的返回类型,第二个参数为方法的名称printName,第三个参数为方法的参数类型数组,第四个参数表示将生成的方法添加到哪个类。
接下来,调用CtMethod的setModifiers()方法来设置printName()方法的访问修饰符,这里将其设置为public。紧接着为printName()方法设置方法体,在方法体中简单的在命令行打印成员变量name。
最后通过CtClass的addMethod()方法将生成的printName方法添加到User类中。
这段代码的效果如下所示。publicvoidprintName(){System。out。println(this。name);}完整案例
为了方便小伙伴们更加清晰的看到完整的源代码,这里我也将完整的源代码贴出来,如下所示。authorbinghe(公众号:冰河技术)version1。0。0description使用Javassist生成一个User类,并测试publicclassCreateUserClass{使用Javassist创建一个User对象publicstaticvoidcreateUser()throwsException{使用默认的ClassPoolClassPoolpoolClassPool。getDefault();1。创建一个空类CtClassctClasspool。makeClass(io。binghe。bytecode。javassist。bean。User);2。新增一个字段privateStringname;字段的名称为nameCtFieldparamnewCtField(pool。get(java。lang。String),name,ctClass);设置访问修饰符为privateparam。setModifiers(Modifier。PRIVATE);设置字段的初始值为binghectClass。addField(param,CtField。Initializer。constant(binghe));3。添加无参的构造函数CtConstructorconstructornewCtConstructor(newCtClass〔〕{},ctClass);constructor。setBody({0。namebinghe;});ctClass。addConstructor(constructor);4。添加有参构造函数constructornewCtConstructor(newCtClass〔〕{pool。get(java。lang。String)},ctClass);constructor。setBody({0。name1;});ctClass。addConstructor(constructor);5。添加getter和setter方法ctClass。addMethod(CtNewMethod。setter(setName,param));ctClass。addMethod(CtNewMethod。getter(getName,param));6。创建一个输出name的方法CtMethodctMethodnewCtMethod(CtClass。voidType,printName,newCtClass〔〕{},ctClass);ctMethod。setModifiers(Modifier。PUBLIC);ctMethod。setBody({System。out。println(name);});ctClass。addMethod(ctMethod);ctClass。writeFile();}}效果演示
编写main方法,直接调用CreateUserClass类的createUser()方法,如下所示。publicstaticvoidmain(String〔〕args)throwsException{CreateUserClass。createUser();}
运行main()方法后,生成了我们想要的User类的字节码,如下所示。
效果符合我们的预期。案例总结
我们使用Javassist动态生成了符合预期的User类对象,通过本文的学习,我们掌握了如何使用Javassist生成JavaBean对象。是不是很简单呢?小伙伴们赶紧打开IDEA搞起来吧。
王者荣耀铠的出装铭文很多人喜欢玩铠,那就说一下铠的铭文出装。铭文:10狩猎10鹰眼10祸源(追求暴效极限的话可以7祸源3无双)出装分为两套。一套打野全输出。一套半肉边路。打……
保护脾胃,就是保护你的命根!一剂壮脾方,身体倍棒,吃嘛嘛香大家好,我是李医生!脾胃是个老生常谈的话题了,为什么总要说它呢,就是因为它实在太重要。都说五脏六腑亲兄弟,气血才是父母亲。气血从哪儿来呢,那就是我们的脾胃了。绕了一圈,最……
二胎父母对子女的爱,为啥总是做不到一碗水端平?现在国家提倡生二胎三胎,很多大孩都会反对,但是大人都会说有了二胎爸爸妈妈还是会一样爱你的,我想问已经有二胎的家长和即将有二胎的家长,你们真的能做到平等对待吗?我就是出生8……
远去的味道儿远去的味道儿活,就要够劲,还要够味。两天来,小雨时断时续,风吹树摇,黄叶满地,房子里也骤然冷了许多。我在衣柜里翻腾御冷的厚衣时,忽然想起不久前给去世的亲人送的寒衣。……
王毅会见马尔代夫外长沙希德【王毅会见马尔代夫外长沙希德】当地时间2022年9月24日,国务委员兼外长王毅在纽约出席联合国大会期间会见马尔代夫外长沙希德。王毅祝贺沙希德担任联大主席期间成功履职,为联合国事……
正式任命!62岁蔡振华走马电影推广大使,刘国梁支持,球迷激动大家好,我是詹妹,我们一起来关注国乒,目前国乒在卡塔尔的亚洲预选赛已经正式开赛,在新的周期也希望拿下好成绩,争取在2023年取得开门红。与此同时国乒也发生了重磅的好消息,我们退……
美国政府把中国联通列入违规名单,吊销其在美经营权美国政府以国家安全理由将中国联通美洲公司列入违规名单,终止其在美国的经营授权。美国联邦通信委员会27日发布命令,吊销中国联通美洲营运有限公司(ChinaUnicom(Am……
让你立刻清醒的文案1最后几天了,过年开始,我就要去喜欢能有结果和值得的人了2。不要愚蠢到拿时间长短来衡量感情。3。我会试着慢慢忘记这一切。然后擦干眼泪扎起高马尾从头再来。4。一……
潘石屹这回真跑了,留下一个烂摊子焦点wumiancaijing。com潘氏夫妇这些年仅现金分红就拿到130多亿,而SOHO中国如今借款超160亿,打折卖项目度日。本文由无冕财经(wumiancaiji……
先睹为快能横着走还能原地旋转,这款氢能概念车将亮相进博会【编者按】迈入第五个年头,中国国际进口博览会再度进入开幕倒计时。作为连接中国与世界的重要纽带,今年的四叶草展馆将迎来哪些全球尖端新品?八方来客又将为这场永不落幕的盛……
科比坠机遗体照遭泄露案判决结果公布科比坠机遗体照遭泄露案判决结果公布,陪审团判给NBA巨星科比布莱恩特的遗孀1600万美元的赔偿金,原因是急救人员拍摄了2020年1月直升机坠毁现场并与公众分享的照片泄露。……
中国财富报道数字人民币APP又更新了!新增两大功能,过年可以视频加载中。。。近日,数字人民币App再次完成版本更新,增加了专属头像、现金红包两大功能。其中,现金红包又包含现金红包、群红包和专属红包三类,分别可通过数字人民币App或……