动态编译
实际上,JAVA提供了API用于动态编译的功能.
动态编译的两种做法
-
通过Runtime调用JAVA,启动新的进程去操作
Runtime runtime = Runtime.getRuntime();
-
Process process = runtime.exec("javac -cp -D:/MyJava/ HelloWorld.java");
通过JavaCompiler动态的进行编译(6.0以后引入的新功能)
public static int compileFile(String sourceFile){
//模拟JAVA源文件地址
sourceFile = "E:/MyJava/HelloWorld.java";
//获取编译器对象
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
/* 执行编译操作
第一个参数inputStream :为JAVA编译器提供参数
第二个参数outputStream :得到JAVA编译器的输出信息
第三个参数OutputStream :接收编译器的错误信息
第四个参数:可变参数(String[]),能传入一个或者多个JAVA源文件的路径
返回值:0表示成功编译,非0表示编译失败
*/
int result = compiler.run(null, System.out, System.out, sourceFile);
if(result == 0){
System.out.println("编译成功");
}else{
System.out.println("编译失败");
}
return result;
}
JAVA-动态运行
还是那个概念,通过JAVA程序去运行JAVA的class文件
两种办法
1,通过RuntimeAPI启用新的线程去执行
Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec("java -cp -D:/MyJava/ HelloWorld.java");
2,通过反射,运行编译好的类
public static void run(String dir,String classFile)throws Exception{
//目标目录
dir = "E:/MyJava/";
//目标class文件
classFile = "HelloWorld";
//创建目录url类
URL[] urls = new URL[]{new URL("file:/"+dir)};
//获取url类加载器
URLClassLoader classLoader = new URLClassLoader(urls);
//加载指定的class文件
Class clazz = classLoader.loadClass(classFile);
//获取其main方法
Method mainMethod = clazz.getMethod("main", String[].class);
/*
执行main方法,传递字空符数组参数
因为main方法是static方法,所以可以不用传递对象
# 强制转换的原因
* 可变参数是jdk5之后的东西
* 如果说不把这个String[]转换为Object的话
* String[]里面的每个参数,都会被当作一个新的String[]被main方法加载
* 而main方法的参数只有一个
*/
mainMethod.invoke(null, (Object)new String[]{});
}
字节码修改
常见的字节码操作类库
1,BCEL 2,ASM 3,CGLIB 4,Javassist
介绍&性能对比
- 其实就是字节码文件的操作,跟反射差不多
- 使JAVA成为动态语言的原因之一
- 运行时操作字节码可以让我们实现如下功能
1,动态生成新的类
2,动态改变某个类的结构(操作属性)
Java动态性的常见两种实现方式
1,字节码操作 2,反射
性能对比
- 比反射开销小,性能高
- ASM > Javaasist性能 > 反射
- 从网上分析结果来看,性能上各种方式的差距不算太大。
* 在易用性方面:
1. JDK代理是最简单方便的,只需要使用Proxy和InvocationHandler两个类,不过只能代理接口。
2. 其次是CGLIB,也很方便,不过需要引入CGLIB的JAR包。
3. Javassist需要用用字符串拼接Java源代码,稍微会比较繁琐。
4. 最麻烦的是ASM,需要手工写字节码,一般人可能还写不出来。
* 在代理对象的创建速度上
1. JDK代理与ASM都很快,比最慢的CGLIB快20倍以上。
2. 考虑到易用性,在对接口进行动态代理时,使用JDK代理应该是最合适的。
3. 在不能使用JDK代理的情况下,可以考虑使用CGLIB或者Javassist。
4. CGLIB的缺点是创建代理对象的速度慢,Javassist的缺点是需要手动编写Java源码。
5. 如果非要在这个两个中选择一个,那么只有在对性能要求非常高的情况下选择Javassist,其他一般情况下,个人认为CGLIB是比较合适的。