java - 动态

动态编译

实际上,JAVA提供了API用于动态编译的功能.

动态编译的两种做法

  1. 通过Runtime调用JAVA,启动新的进程去操作
    Runtime runtime = Runtime.getRuntime();

  2. 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是比较合适的。
上一篇:深入理解java动态代理的两种实现方式(JDK/Cglib)


下一篇:java jdk与cglib代理代码实现