做者:小傅哥 博客:https://bugstack.cn

沉淀、分享、生长,让本身和别人都能有所收成!

一、媒介

在上一篇 Helloworld 中,我们初步测验考试利用了 Javassist字节编程的体例,来创建我们的办法体并通过反射挪用运行告终果。大致领会到创建在利用字节码编程的时候根本离不开三个核心类;ClassPool、CtClass、CtMethod,它们别离办理着对象容器、类和办法。但是我们还少用一样就是字段;CtFields,在那一章节中我们不行会利用字段,还会创建多个差别入参类型和返回值的进修。

在进修之前先重点列一下相关的常识点,如下;

CtClass.doubleTyPE、intType、floatType等 8 个根本类型和一个voidType,也就是空的返回类型。传递和返回的是对象类型时,那么需要时用;pool.get(Double.class.getName(),停止设置。当需要设置多个入参时,需要在数组中以此设置入参类型;new CtClass[]{CtClass.doubleType, CtClass.doubleType}。在办法体中需要获得入参并计算时,需要利用 $1、$2 ...,数字暗示入参的位置。$0 是 this。CtField 设置属性字段,并赋值。Javassist 中的拆箱/拆箱。

好!那么我们就起头对那些常识点停止应用,创建出类和对应的办法。「所有代码都能够存眷公家号:bugstack虫洞栈,回复码下载获取」

二、开发情况JDK 1.8.0javassist 3.12.1.GA<dependency> <groupId>javassist</groupId> <artifactId>javassist</artifactId> <version>3.12.1.GA</version> <type>jar</type></dependency>三、案例目的

为了操练属性字段和办法的差别的入参、出参,我们利用 javassist 创建如下如许的办法。当然你也能够测验考试去扩展其他类型的办法。

public class ApiTest { PRivate double π = 3.14D; //S = πr² public double calculateCircularArea(int r) { return π * r * r; } //S = a + b public double sumOfTwoNumbers(double a, double b) { return a + b; }}四、手艺实现

GenerateClazzMethod.java & 生成类和办法

/** * 公家号:bugstack虫洞栈 * 博客栈:https://bugstack.cn - 沉淀、分享、生长,让本身和别人都能有所收成! * 本专栏是小傅哥多年处置一线互联网Java开发的进修过程手艺汇总,旨在为各人供给一个明晰详细的进修教程,偏重点更倾向编写Java核心内容。若是能为您供给帮忙,请赐与撑持(存眷、点赞、分享)! */public class GenerateClazzMethod { public static void main(String[] args) throws CannotCompileException, NotFoundException, IOException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { ClassPool pool = ClassPool.getDefault(); CtClass ctClass = pool.makeClass("org.itstack.demo.javassist.MathUtil"); // 属性字段 CtField ctField = new CtField(CtClass.doubleType, "π", ctClass); ctField.setModifiers(Modifier.PRIVATE + Modifier.STATIC + Modifier.FINAL); ctClass.addField(ctField, "3.14"); // 办法:求圆面积 CtMethod calculateCircularArea = new CtMethod(CtClass.doubleType, "calculateCircularArea", new CtClass[]{CtClass.doubleType}, ctClass); calculateCircularArea.setModifiers(Modifier.PUBLIC); calculateCircularArea.setBody("{return π * $1 * $1;}"); ctClass.addMethod(calculateCircularArea); // 办法;两数之和 CtMethod sumOfTwoNumbers = new CtMethod(pool.get(Double.class.getName()), "sumOfTwoNumbers", new CtClass[]{CtClass.doubleType, CtClass.doubleType}, ctClass); sumOfTwoNumbers.setModifiers(Modifier.PUBLIC); sumOfTwoNumbers.setBody("{return Double.valueOf($1 + $2);}"); ctClass.addMethod(sumOfTwoNumbers); // 输出类的内容 ctClass.writeFile(); }}

那里面有几个核心点,讲解如下;

CtField,属性字段的创建。那就像我们一般写代码一样,需要设定属性的;名称、类型以及是 public 的仍是 private 的以及 static 和 final 等。都能够通过 Modifier.PRIVATE + Modifier.STATIC + Modifier.FINAL,通过组合来控造。同样那也适用于对办法类型的设置。同时需要在添加属性的处所,设置初始值。接下来是我们设置了一个求圆面积的办法,若是说在办法体中需要利用到入参类型。那么需要通过符号 $+数字,来获取入参。那个数字就是当前入参的位置。好比取第一个入参:$1,以此类推。之后是我们的多种入参类型,在那起头我们也提到了。若是是根本类型入参都能够利用 CtClass.doubleType,对象类型入参利用 pool.get(类.class.getName) 获取。最末同样我们会把利用字节码编译的 class 输出到工程目次下 ctClass.writeFile()。在Javassist中其实不会给类型做拆箱和拆箱操做,需要显式的处置。例如上面案例中,需要将 double 利用 Double.valueOf 停止转换。

下面那张根本描述了一个类办法在创建时候差别参数的含义,能够参考。

通过字节码编程,在程序中动态的创建方法  第1张

Javassist 创建类办法入参描述

五、测试成果1. 反射挪用字节码类办法

在测试之前,我们需要写一点反射代码来挪用类的办法

// 测试挪用Class clazz = ctClass.toClass();Object obj = clazz.newInstance();Method method_calculateCircularArea = clazz.getDeclaredMethod("calculateCircularArea", double.class);Object obj_01 = method_calculateCircularArea.invoke(obj, 1.23);System.out.println("圆面积:" + obj_01);Method method_sumOfTwoNumbers = clazz.getDeclaredMethod("sumOfTwoNumbers", double.class, double.class);Object obj_02 = method_sumOfTwoNumbers.invoke(obj, 1, 2);System.out.println("两数和:" + obj_02);

测试成果:

圆面积:4.750506两数和:3.0Process finished with exit code 02. 查看利用Javassist生成的类通过字节码编程,在程序中动态的创建方法  第2张

Javassist 生成的类内容

六、总结本篇案例中重点强调了属性字段创建,同时需要给属性字段赋值。在 Javassist 是不会停止类型的主动拆箱和拆箱的,需要我们停止手动处置,不然生成类在施行会报类型错误。当需要利用入参的时候,能够利用 $1 来获取。那也是后续做一些监控获取入参的办法。