范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

字节码编程丨使用Javassist生成JavaBean

  这种方式几乎不需要修改源程序就能够达到我们想要的效果。今天,我们就一起使用Javassist来动态生成JavaBean对象。
  掌握这个知识点后以便后续我们在手撸DAPM(分布式性能管理系统)时能够动态生成JavaBean对象来反序列化客户端发送的数据,或者从服务端响应回来的数据。  开发环境JDK 1.8  IDEA 2018.03  Maven 3.6.0  Maven依赖
  在项目的pom.xml文件中添加如下环境依赖。       3.20.0-GA                 org.javassist         javassist         ${javassist.version}       案例效果
  整体案例的效果比较简单,就是通过运行我们写的程序,能够动态生成User类的class字节码。如下所示。  package io.binghe.bytecode.javassist.bean;  public class User {     private String name = "binghe";      public User() {         this.name = "binghe";     }      public User(String var1) {         this.name = var1;     }      public void setName(String var1) {         this.name = var1;     }      public String getName() {         return this.name;     }      public void printName() {         System.out.println(this.name);     } } 在这个User类中,有一个成员变量name,默认值为binghe。  分别有一个无参构造方法和有参构造方法。  成员变量name的get/set方法。  打印成员变量name的方法printName()。
  了解完案例的效果后,我们就开始动手实现如何动态生成这个User类。  案例实现
  具体的案例实现,我们可以参考案例的效果一步步完成,这里,我们可以将整个User类的动态生成过程分为6个步骤,分别为:  创建User类。  添加name字段。  添加无参构造方法。  添加有参构造方法。  添加get/set方法。  添加printName()方法。
  好了,说干就干,接下来就按照这5个步骤动态生成User类。  创建User类//使用默认的ClassPool ClassPool pool = ClassPool.getDefault();  //1.创建一个空类 CtClass ctClass = pool.makeClass("io.binghe.bytecode.javassist.bean.User");
  User类的创建方法和我们之前创建HelloWorld的类是相同的,首先是获取一个ClassPool对象,通过调用ClassPool对象的makeClass方法创建User类。  添加name字段//2.新增一个字段 private String name; 字段的名称为name CtField param = new CtField(pool.get("java.lang.String"), "name", ctClass); //设置访问修饰符为private param.setModifiers(Modifier.PRIVATE); //设置字段的初始值为binghe ctClass.addField(param, CtField.Initializer.constant("binghe"));
  为User类添加成员变量name时,使用了Javassist中的CtField类。这里,我们使用的CtField的构造方法的第一个参数是成员变量的类型,第二个参数是变量的名称,第三个字段表示将这个变量添加到哪个类。
  创建完CtField对象param后,我们调用了param的setModifiers()方法设置访问修饰符,这里将其设置为private。
  接下来,为成员变量name赋默认值binghe。上述代码生成的效果如下所示。  private String name = "binghe"; 添加无参构造方法//3.添加无参的构造函数 CtConstructor constructor = new CtConstructor(new CtClass[]{}, ctClass); constructor.setBody("{" +                     " $0.name = "binghe"; " +                     "}"); ctClass.addConstructor(constructor);
  添加无参构造方法时,使用了Javassist中的CtConstructor类,第一个参数是动态生成的目标类的构造方法的参数类型数组,第二个参数表示将构造方法添加到哪个类中。
  接下来,通过调用CtConstructor的setBody()方法设置无参构造方法的方法体。这里需要注意的是方法体中只有一行代码时,可以省略 {} , 但是为了防止出错,冰河强烈建议无论方法是否只有一行代码,都不要省略 {} 。
  细心的小伙伴肯定会发现在方法体中通过 $0 引用了成员变量name,估计小伙伴们也猜到了这个 $0  是干啥的。没错,它在生成User类后会被编译成this 。
  在Javassist中,还会有一些其他具有特定含义的符号,这个我们在文章的最后统一说明。
  这段代码的效果如下所示。  public User() {     this.name = "binghe"; }
  接下来,就是调用CtClass的addConstructor()方法为User类添加无参构造方法。  添加有参构造方法//4.添加有参构造函数 constructor = new CtConstructor(new CtClass[]{pool.get("java.lang.String")}, ctClass); constructor.setBody("{" +                     "$0.name = $1;" +                     "}"); ctClass.addConstructor(constructor);
  添加有参构造方法的整体流程和添加无参构造方法的整体流程相同,只是在创建CtConstructor对象时,在CtConstructor的构造方法的第一个参数类型数组中使用 pool.get("java.lang.String") 添加了一个数组元素,表示生成的目标类的构造方法存在一个String类型的参数。
  另外,在设置方法体时,使用了如下代码。  $0.name = $1;
  表示将构造方法的第一个参数赋值给成员变量name。这里, $0  表示 this , $1  表示第一个参数,$2 表示第二个参数,以此类推。
  这段代码的效果如下所示。  public User(String var1) {     this.name = var1; } 添加get/set方法//5.添加getter和setter方法 ctClass.addMethod(CtNewMethod.setter("setName", param)); ctClass.addMethod(CtNewMethod.getter("getName", param));
  添加get/set方法就比较简单了,直接使用CtClass的addMethod()添加,使用CtNewMethod的setter()方法生成set方法,其中,第一个参数为生成的方法的名称setName,第二个参数表示是为哪个字段生成setName方法。
  使用CtNewMethod的getter()方法生成get()方法,第一个参数为生成的方法的名称getName,第二个参数表示是为哪个字段生成getName方法。
  这段代码的效果如下所示。  public void setName(String var1) {     this.name = var1; }  public String getName() {     return this.name; } 添加printName()方法//6.创建一个输出name的方法 CtMethod ctMethod = new CtMethod(CtClass.voidType, "printName", new CtClass[]{}, 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类中。
  这段代码的效果如下所示。  public void printName() {     System.out.println(this.name); } 完整案例
  为了方便小伙伴们更加清晰的看到完整的源代码,这里我也将完整的源代码贴出来,如下所示。  /**  * @author binghe (公众号:冰河技术)  * @version 1.0.0  * @description 使用Javassist生成一个User类, 并测试  */ public class CreateUserClass {      /**      * 使用Javassist创建一个User对象      */     public static void createUser() throws Exception{         //使用默认的ClassPool         ClassPool pool = ClassPool.getDefault();          //1.创建一个空类         CtClass ctClass = pool.makeClass("io.binghe.bytecode.javassist.bean.User");          //2.新增一个字段 private String name; 字段的名称为name         CtField param = new CtField(pool.get("java.lang.String"), "name", ctClass);         //设置访问修饰符为private         param.setModifiers(Modifier.PRIVATE);         //设置字段的初始值为binghe         ctClass.addField(param, CtField.Initializer.constant("binghe"));          //3.添加无参的构造函数         CtConstructor constructor = new CtConstructor(new CtClass[]{}, ctClass);         constructor.setBody("{" +                 " $0.name = "binghe"; " +                 "}");         ctClass.addConstructor(constructor);          //4.添加有参构造函数         constructor = new CtConstructor(new CtClass[]{pool.get("java.lang.String")}, ctClass);         constructor.setBody("{" +                 "$0.name = $1;" +                 "}");         ctClass.addConstructor(constructor);          //5.添加getter和setter方法         ctClass.addMethod(CtNewMethod.setter("setName", param));         ctClass.addMethod(CtNewMethod.getter("getName", param));          //6.创建一个输出name的方法         CtMethod ctMethod = new CtMethod(CtClass.voidType, "printName", new CtClass[]{}, ctClass);         ctMethod.setModifiers(Modifier.PUBLIC);         ctMethod.setBody("{" +                 "System.out.println(name);" +                 "}");         ctClass.addMethod(ctMethod);          ctClass.writeFile();     } } 效果演示
  编写main方法,直接调用CreateUserClass类的createUser()方法,如下所示。  public static void main(String[] args) throws Exception {     CreateUserClass.createUser(); }
  运行main()方法后,生成了我们想要的User类的字节码,如下所示。
  效果符合我们的预期。  案例总结
  我们使用Javassist动态生成了符合预期的User类对象,通过本文的学习,我们掌握了如何使用Javassist生成JavaBean对象。是不是很简单呢?小伙伴们赶紧打开IDEA搞起来吧。

苹果成5G市场最大赢家!iPhone销量收入无敌了,华为却让人心疼众所周知,在一线手机品牌中,苹果是最晚步入5G时代的手机厂商,像华为三星小米等厂商在2019年下半年就推出了首款5G智能手机,而苹果直到2020年10月才推出第一款支持5G网络的i新鲜早科技丨微软160亿美元收购Nuance获欧盟批准传蘑菇街技术部门裁员80商汤科技IPO已获超额认购21世纪经济报道记者杨清清实习生陈龙潼综合报道早上好,新的一天又开始了。在过去24小时内,科技行业发生了哪些有意思的事情?来跟21tech一起看看吧。巨头风向标1微软160亿美元收JZ把数组排成最小的数把数组排成最小的数题目描述输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组3,32,321,则打印出这三个数字能排成的最小数Java加密解密和数字签名在做项目中,只要涉及敏感信息,或者对安全有一定要求的场景,都需要对数据进行加密。在Java中原生API即可实现对称加密与非对称加密,并支持常用的加密算法。对称加密对称加密使用单钥完辛巴起诉快手,此前称其限制自己直播间流量出品壹览商业编辑樊益宁12月21日,壹览商业获悉,北京快手科技有限公司新增两则开庭公告,分别为辛有志与北京快手科技有限公司相关网络侵权责任纠纷时诗与北京快手科技有限公司相关网络侵权爱奇艺,快手,字节跳动疯狂裁员!面对996和裁员潮,员工太难了很多人看到爱奇艺快手字节跳动疯狂裁员的新闻,都在说是互联网的红利期过去了。早在12月之前,就有网友爆料快手将拿高薪员工先开刀,没想到竟一语成谶。在12月1日,爱奇艺就裁掉了2040强悍双芯2。0iQOONeo5S致力高帧低功耗游戏体验2021年12月20日,iQOONeo系列最新产品iQOONeo5SiQOONeo5SE正式发布。iQOONeo5S强悍双芯2。0再进化,高通骁龙888与独显芯片Pro两颗芯片,强这些技术将是塞尔维亚的未来探访贝尔格莱德机器人和人工智能教育中心来源人民网国际频道原创稿人民网贝尔格莱德12月20日电(记者韩硕)在首都贝尔格莱德南郊,毗邻南斯拉夫历史博物馆处,坐落着一幢被绿色草坪和浓密树木环抱的三层白色小楼。这里是由中国企业中国数字技术出海蚂蚁牵手汉堡王母公司把数字解决方案带去亚太8国据亚洲科技媒体据了解,蚂蚁集团将为RBI旗下除中国市场外的亚太8国1500家连锁餐厅提供一系列数字解决方案加速其数字转化型,包括小程序Saas解决方案以及跨境移动支付和营销解决方案学而思K9非营利性机构乐读优课上线实习记者陈振芳新K9app可以下载了。12月20日,乐读优课在小红书发布上述消息并称,乐读是经教育主管部门批准成立的非盈利性学科类培训机构,将在2022年1月开始面向全国中小学生提第三季度5G手机销量华为占30。7市场,苹果占12。5不及华为一半现如今,手机已经成为大家日常生活中的必需品,可以应用的领域已经不再局限于打电话。值得注意的是,在最近,2021年第三季度5G手机销量的榜单已经发布,在这其中,华为获得了出色的成绩,
奥特曼赛罗形态盘点赛罗奥特曼是赛文奥特曼的亲生儿子,但赛文并没有告诉赛罗自己是赛罗的亲生父亲。赛罗曾经为了证明自己的实力而意图独占等离子火花塔的能量(等离子火花核心),但被赛文阻止,后托付给雷欧。为十大远古海洋巨兽沧龙中生代海洋中最大的顶级掠食者。虽然它的历史很短(从陆地上的崖蜥进化而来,在白垩纪中晚期才出现并且迅速繁衍,随后和恐龙一起灭绝),但却一路平步青云,把比它历史早远得多的海洋爬行动奥特曼幽蓝魅影托雷基亚奥特曼托雷基亚这个名字在光之国意为癫狂的好奇心,揭示了他为追寻真理却最终陷入黑暗而疯狂的不归路。托雷基亚奥特曼曾经是光之国的科学家,是希卡利奥特曼的下级。是泰罗奥特曼的朋友,后来在追求善世界十大猛犬猛犬霸主中国藏獒藏獒,又名西藏獒獒犬番狗龙狗。原产于中国青藏高原,是一种高大凶猛垂耳的猛犬。身长约120厘米左右,体毛粗硬,丰厚,外层披毛不太长,底毛在寒冷的气候条件下,则浓密且软九头蛇许德拉希腊神话中的怪物九头蛇是一种传说中有九个头的大蛇在许多文化中都存在。欧洲传统中的九头蛇名为希腊语,英语Hydra(译为海德拉,意为水蛇)。传说生物,在古希腊神话里出现最为频繁,另外波斯古经圣经非洲古代十大名曲中华古韵,向有中国十大名曲一说。分别为高山流水广陵散平沙落雁梅花三弄十面埋伏夕阳箫鼓渔樵问答胡笳十八拍汉宫秋月和阳春白雪。据专家考证,这些古代中国名曲的原始乐谱大都失传,今天流传的数码宝贝盘点皇家骑士奥米加兽形态及亚种病毒克星的战斗暴龙兽及金属加鲁鲁兽,因期待善行的人们的强大意志而融合,诞生出皇家骑士其中一员的圣骑士型数码兽。兼具两者特性的数码兽,是无论任何状况下都能充分发挥自身能力的复合型战士数码宝贝暴龙兽系列数码兽暴龙兽等级成熟期类型恐龙型属性疫苗种头部的皮肤硬化,覆盖着像甲虫一样的壳的恐龙型数码兽。拥有锐利的爪巨大的角等如全身凶器般的身体,是非常有攻击性的数码兽。但是,智力很高,如果能驯服捷德奥特曼的史上最强之敌终极审判者吉尔巴里斯克西亚人为了使宇宙获得永恒的和平而制造的人工头脑,是君临拥有大量加拉特隆的吉尔巴利斯军团的顶端者的存在。在还是未完全体的时候是类似于圆筒柱型的电脑,不具备任何攻击力,曾在被泰罗追击还记得数码I中保护过美美和嘉儿的鼻涕兽吗?居然有究极体鼻涕兽鼻涕兽有着像蛞蝓(注俗称鼻涕虫)一样身体的软体型数码兽。喜欢阴暗潮湿的地方,又不厉害的软体型成熟期数码宝贝,通常用自己的大便来当武器,绝招是便便投射。绝招不具有杀伤力,但是却很恶心数码宝贝天女兽的女神进化路线天女兽拥有美丽女性身姿的大天使型数码兽。以前被分类为天使型,但因为其高能力而被明确是大天使型。特征就是成熟期的天使拥有六片羽翼,而完全体的天使则拥有八片羽翼。性格相当温和,但是她绝