菜鸟学习笔记:Java提升篇12(Java动态性2——动态编译、javassist字节码操作)

  • Java的动态编译
  • 通过脚本引擎执行代码
  • Java字节码操作
    • JAVAssist的简单使用
    • 常用API
      • 类操作
      • 方法操作
      • 方法加强
      • 获取构造器
      • 获取注解
  • 后语

Java的动态编译

Java的动态编译是指在Java程序运行时动态的执行编译指令进而执行另一段Java代码,它是在Java6.0中引入的机制。
对于Java动态编译有两种做法,一种是通过Runtime调用Javac,启动新的进程进行操作,比如:

Runtime run = Runtime.getRuntime();
Process process = run.exec("javac HelloWorld.java");

另一种是通过JavaCompiler动态编译,

public static void main(String[] args) throws IOException {JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();int result = compiler.run(null, null, null, "D:\\HelloWorld.java");//这里四个参数中://第一个参数为一个流对象,表示为java编译器提供的参数,如果为null表示System.in//第二个参数表示得到Java编译器的输出信息//第三个编译器代表接收编译器的错误消息//第四个参数为一个字符串或字符串数组,表示传入的Java源文件//返回值是一个int类型的值,0代表编译成功,非0代表编译失败//执行完成后会生成class文件System.out.println(result == 0 ? "编译成功" : "编译失败");//执行java 命令 , 空参数, 所在文件夹Process process = Runtime.getRuntime().exec("java HelloWorld");BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));String str;while ((str = bufferedReader.readLine()) != null) {System.out.println(str);}}

执行后生成的文件:


我们也可以通过反射来运行编译好的类:

 public static void main(String[] args) throws IOException {try {  URL[] urls = new URL[] { new URL("file:/" + "D:/HelloWorld") };//加载class文件  URLClassLoader loader = new URLClassLoader(urls);  // 通过反射调用此类  Class clazz = loader.loadClass("HelloWorld");  Method m = clazz.getMethod("main", String[].class);  // 由于可变参数是jdk5.0之后才有,上面代码会编译成m.invoke(null,"aa","bb");会发生参数不匹配的问题  // 因此必须加上Object 强转  m.invoke(null, (Object) new String[] {});  } catch (Exception e) {  e.printStackTrace();  }}

执行结果与上例相同。

通过脚本引擎执行代码

Java脚本引擎是JDK6.0之后添加的新功能,它使得Java可以通过一套固定的接口与各种脚本引擎交互,从而达到在Java平台上调用各种脚本语言的目的,是Java联通其他脚本语言的桥梁,使得Java可以把一些复杂业务交给脚本语言处理,大大提高了开发效率 。
以Javascript为例,Java脚本API可以获取脚本程序的输入,通过脚本引擎运行脚本返回运行结果。Java调用JS运用的是Rhino,它是Java编写的Javascript开源实现。通过脚本引擎的运行上下文可以在脚本和Java平台之间交换数据。调用方式如下:

ScriptEngineManager sem = new ScriptEngineManager();
ScriptEngine engine = sem.getEngineByName("javascript");

运用engine对象就可以实现Java与JS的交互。

 public static void main(String[] args) throws Exception {// 获得脚本引擎对象ScriptEngineManager sem = new ScriptEngineManager();ScriptEngine engine = sem.getEngineByName("javascript");// 定义变量,存储到引擎的上下文中engine.put("msg", "hello~world!");String str = "var user= {name:'HanQuan', age:29};";str += "print(user.name);";// 执行脚本engine.eval(str);engine.eval("msg = 'aaa'");System.out.println(engine.get("msg"));//定义函数engine.eval("function add(a,b){var sum = a + b; return sum;}");//执行函数//取得接口Invocable jsInvocable = (Invocable)engine;//执行脚本中定义的方法Object result = jsInvocable.invokeFunction("add", new Object[] {22,33});System.out.println(result);}

程序结果:

Java字节码操作

Java动态性的实现有两种方式,一种是之前所讲的反射,另一种就是所谓的字节码操作,相对反射而言字节码操作的开销要小于反射,使得其效能高于反射机制。
字节码操作的含义就是通过程序对Java代码编译新城的class文件进行操作。Java中涉及字节码操作的类库有BCEL、ASM、CGLIB、Javassist,其中BCEL和ASM直接涉及到JVM底层的操作和指令,效率较高,CGLIB基于ASM实现,效率相对可以。而JAVAssist是一个开源的分析、编辑和创建Java字节码的类库。性能与CGLIB相似,但它的特点是使用简单。本次学习就是基于JAVAssist。

JAVAssist的简单使用

JAVAssist在使用时需要导入Javassist.jar包。下载官网。
我们直接通过一改简单实例来创建一个Person类:

public static void main(String[] args) throws Exception {public static void main(String[] args) throws Exception {ClassPool pool = ClassPool.getDefault();CtClass personClass = pool.makeClass("eee.Person");//创建属性CtField idField = CtField.make("private int id;", personClass);personClass.addField(idField);//创建方法CtMethod m1 = CtMethod.make("public int getId(){return id;}", personClass);CtMethod m2 = CtMethod.make("public void setId(int id){this.id = id;}", personClass);personClass.addMethod(m1);personClass.addMethod(m2);//添加构造器CtConstructor constructor = new CtConstructor(new CtClass[]{CtClass.intType},personClass);constructor.setBody("{this.id = id;}");personClass.addConstructor(constructor);personClass.writeFile("c:/aaa");System.out.println("sss");}
}

实验结果:

常用API

类操作

通过javassist可以对类进行操作:

 public static void main(String[] args) throws Exception {ClassPool pool = ClassPool.getDefault();CtClass personClass = pool.makeClass("eee.Person");byte[] bytes = personClass.toBytecode();   System.out.println(Arrays.toString(bytes));System.out.println("类名:"+personClass.getName());//获取类名System.out.println("简要类名:"+personClass.getSimpleName());//获取简要类名System.out.println("父类:"+personClass.getSuperclass());//获取父类System.out.println("接口:"+personClass.getInterfaces());//获取接口}

程序结果:

方法操作

 public static void main(String[] args) throws Exception {ClassPool pool = ClassPool.getDefault();CtClass personClass = pool.makeClass("eee.Person");//创建属性的另一种方式CtField idField = new CtField(CtClass.intType, "id", personClass);idField.setModifiers(Modifier.PRIVATE);//属性修饰符为privatepersonClass.addField(idField);//创建方法的另一种方式CtMethod method = new CtMethod(CtClass.intType, "add", new CtClass[] {CtClass.intType,CtClass.intType}, personClass);method.setModifiers(Modifier.PUBLIC);//方法修饰符为publicmethod.setBody("{System.out.println(\"I am person\");return $1+$2;}");//创建方法体,其中$1和$2分别代表第一第二个参数personClass.addMethod(method);//通过反射生成新的方法Class clazz = personClass.toClass();Object obj = clazz.newInstance();Method m = clazz.getDeclaredMethod("add", int.class, int.class);Object result = m.invoke(obj, 200, 300);System.out.println(result);}

程序结果:

方法加强

通过javassist可以对类中已有的方法进行加强,也就是在已有方法中添加其他操作,我们首先定义一个Person类:

public class Person {//私有属性private String name;//公有属性public  Integer age;//无参构造public Person() {}//有参构造public Person(String name, Integer age) {this.name = name;this.age = age;}//私有方法private void method1(){System.err.println("method1——run");}//公有方法public void method2(String param){System.err.println("method1=2——run :"+param);}@Overridepublic String toString() {return "Proson{" +"name='" + name + '\'' +", age=" + age +'}';}
}

对该类的method1进行加强:

 public static void main(String[] args) throws Exception {ClassPool pool = ClassPool.getDefault();CtClass personClass = pool.get("eee.Person");CtMethod method = personClass.getDeclaredMethod("method2", new CtClass[] {pool.get(String.class.getName())});method.insertBefore("System.out.println(\"方法增强了\");");//通过反射生成新的方法Class clazz = personClass.toClass();Object obj = clazz.newInstance();Method m = clazz.getDeclaredMethod("method2", String.class);Object result = m.invoke(obj, "aaa");}

程序结果:

获取构造器

 public static void main(String[] args) throws Exception {ClassPool pool = ClassPool.getDefault();CtClass personClass = pool.get("eee.Person");//获取后也可以对构造器进行加强CtConstructor[] cs = personClass.getConstructors();for (CtConstructor c : cs) {System.out.println(c.getLongName());}}

结果:

获取注解

以上一篇文章中讲解的Student类为例:

@Target(ElementType.TYPE)//只能在类型或接口前
@Retention(RetentionPolicy.RUNTIME)//运行期保留,只有这样的注解才能被反射获取
@interface TableStudnet{String value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Filed{String columnName();String type();int length();
}
@TableStudnet("student")
public class Student{@Filed(columnName = "db_id",type="int",length=10)private int id;@Filed(columnName = "db_age",type="int",length=10)private int age;@Filed(columnName = "db_name",type="varchar",length=3)private String name;
}

获取注解的方法

public class test2 {public static void main(String[] args) throws Exception {ClassPool pool = ClassPool.getDefault();CtClass personClass = pool.get("eee.Student");Object[] all = personClass.getAnnotations();TableStudnet s = (TableStudnet)all[0];String value = s.value();System.out.println("value:" + value);}
}

程序结果:

对于javassist中常用的内容大概就是这些,如果大家想深入去探究可以去官网查阅API,当然这些对于平时的运用已经足够了。

后语

在新年即将来临之际,这次的Java系列的学习笔记就全部结束了。相信陪我一起学到这里的小伙伴一定收获丰厚把,哈哈。当然Java的生态链实在太广了,以后需要学习的内容远远不止这些,不过大家也不用担心,不管是什么花里胡哨的框架,它的底层是不变的,也许个人能力有限有些东西讲述的不明确,但笔记中提到的点都是需要大家掌握的。如果读笔记实在学不会可以看看视频。认真将这些知识点过上几遍。相信不管什么框架都可以轻松上手。
从明年开始本人也要开始一轮新知识的学习了,可能以后不会像这次一样完整的写一篇教程。但对于今后会遇到的一些共性问题也会做些说明。最后祝大家在新的一年里平平安安、顺顺利利、心想事成、工资翻倍!
上一篇:菜鸟学习笔记:Java提升篇11(Java动态性1——注解与反射)

菜鸟学习笔记:Java提升篇12(Java动态性2——动态编译、javassist字节码操作)相关推荐

  1. 决策树算法学习笔记(提升篇)

    声明:本文虽有部分自己理解成分,但是大部分摘自以下链接. 决策树(decision tree)(三)--连续值处理 决策树参数讲解+实例 数据挖掘十大算法 C4.5算法的改进: 用信息增益率来选择属性 ...

  2. 菜鸟学习笔记:Java提升篇11(Java动态性1——注解与反射)

    Java提升篇11(Java其它高级特性--注解与反射) 注解(Annotation) JDK内置注解 自定义注解 元注解(meta-annotation) 反射(reflection) 动态语言 反 ...

  3. Java学习笔记之基础篇

    Java学习笔记之基础篇 目录 Java如何体现平台的无关性? 面向对象(OO)的理解 面向对象和面向过程编程的区别 面向对象三大特征 静态绑定和动态绑定(后期绑定) 延伸:类之间的关系 组合(聚合) ...

  4. 菜鸟学习笔记:Java提升篇3(容器3——泛型、排序)

    菜鸟学习笔记:Java容器3--泛型.排序 泛型 泛型类 泛型接口 泛型方法 泛型继承 通配符"?" 泛型知识点补充 容器排序 Comparable接口与compareTo方法 C ...

  5. 菜鸟学习笔记:Java提升篇10(网络2——UDP编程、TCPSocket通信、聊天室案例)

    菜鸟学习笔记:Java提升篇10(网络2--UDP编程.TCPSocket通信) UDP编程 TCP编程(Socket通信) 单个客户端的连接 多个客户端的连接(聊天室案例) UDP编程 在上一篇中讲 ...

  6. 菜鸟学习笔记:Java提升篇9(网络1——网络基础、Java网络编程)

    菜鸟学习笔记:Java提升篇9(网络1--网络基础.Java网络编程) 网络基础 什么是计算机网络 OS七层模型 Java网络编程 InetAddress InetSocketAddress URL类 ...

  7. 菜鸟学习笔记:Java提升篇8(线程2——线程的基本信息、线程安全、死锁、生产者消费者模式、任务调度)

    菜鸟学习笔记:Java提升篇8(线程2--线程的基本信息.线程安全.死锁.生产者消费者模式.任务调度) 线程的基本信息 线程同步 线程安全 死锁 生产者消费者模式 任务调度(了解) 线程的基本信息 J ...

  8. 菜鸟学习笔记:Java提升篇6(IO流2——数据类型处理流、打印流、随机流)

    菜鸟学习笔记:Java IO流2--其他流 字节数组输入输出流 数据类型处理流 基本数据类型 引用类型 打印流 System.in.System.out.System.err 随机流RandomAcc ...

  9. 菜鸟学习笔记:Java提升篇5(IO流1——IO流的概念、字节流、字符流、缓冲流、转换流)

    菜鸟学习笔记:Java IO流1--IO流的概念.字节流.字符流.缓冲流.转换流 IO流的原理及概念 节点流 字节流 文件读取 文件写出 文件拷贝 文件夹拷贝 字符流 文件读取 文件写出 处理流 缓冲 ...

最新文章

  1. 丰度决定了细菌在复杂群落中的功能作用
  2. JVM调优之实战案例(六)(转载)
  3. java编程代码大全_掌握Java编程技巧,代码重构
  4. 【学习笔记】产品成本计划(Product Cost Planning)01
  5. PHP项目部署在tomcat,在Tomcat中部署Web项目的操作方法(必看篇)
  6. 请你简要说明一下线程的基本状态以及状态之间的关系?
  7. kubectl 命令管理(1)
  8. firewall防火墙
  9. 【转载】/etc/passwd /etc/shadow 详解
  10. Java 多线程(一) 基础知识与概念
  11. postgresql 远程用户_构建Python pandas基于SSH远程MySQL和PostgreSQL的数据分析
  12. 接口测试--apipost如何自定义header中的content-type
  13. redis集群出现JedisNoReachableClusterNodeException异常(No reachable node in cluster)
  14. python学习笔记之读取配置文件【转自https://my.oschina.net/u/3041656/blog/793467】
  15. 博士后到底值不值得做?!
  16. load.php 迅雷,C#_C#实现调用迅雷下载的方法,迅雷下载是目前使用非常普遍 - phpStudy...
  17. windows下qt的环境配置
  18. 记录Flutter安装使用过程
  19. form 表单验证验证方法-validate-methods.js
  20. elementUi el-dialog 对话框实现可拖拽、去掉覆盖层、并可操作底层的按钮

热门文章

  1. mysql被更新失败_更新mysql出错:出错原因 You are using safe update mode
  2. android没有界面app,Android离线打包 app处于原生界面 切换到桌面再点击app 原生界面被关闭...
  3. 3904三极管是什么功能_什么是放大器。它有什么特性
  4. java自带的jvm在哪里看_使用jdk工具查看jvm笔记
  5. 用Visual Studio 2008编写Win32汇编程序
  6. 分布式 基本理论 BASE
  7. Nginx之rewrite配置
  8. linux rac 查询ip,你知道RAC怎么增加管理IP地址吗?
  9. (05)System Verilog 数组类型详解
  10. 微信分享签名无效php_php实现微信分享朋友链接功能