Java动态编译
2016-08-05 15:50
330 查看
背景:
我正在开发一个平台,其中一个模块需要由用户来创建,也就是用户可以通过一些选项,系统生成可执行代码,并且可运行在当前平台中。
条件:
项目运行于tomcat中,并且平台所编译的class文件全部被打包成一个jar文件(这个是非常的不爽的,为什么,下面会讲)。所有的外部jar文件都在lib下
思路:
错误思路一:一开始我是准备生成java文件,想当然的觉得系统可以自动编译成class文件(不是手动编译),我明显就是too young too simple。因为你开发的时候,有的平台是可以的,因为它们是热部署的,但是tomcat中明显是不可以的。错误思路二:生成java文件之后,然后调用java的api接口JavaCompiler进行手动编译(各种问题,一把辛酸泪),然后放到项目的正确位置(也就是包路径),但是我忽略了jar这个文件特殊性,jar本质是一个文件,并不是一个目录,java只有读取和修改的权利,并没有添加的权利。所以完全行不通,而且在项目的运行中,对jar进行操作也是非常的安全,非常的不推荐。
正确思路:既然生成了class文件,我为什么一定要放到项目文件中呢,完全可以放到别的文件夹,然后在系统需要运行该文件时,动态加载该文件。
代码:
1.动态编译java文件
//编译依赖的包目录 public final static String jarPath = "xxx\xxxx\"; //存放class文件目录 public final static String directoryPath = "xxx\xxxx\"; public static Boolean operate(String fileName, String source) throws Exception{ //编译器 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); //不想生成java文件,所以存储在内存中 StringWriter writer = new StringWriter(); PrintWriter out = new PrintWriter(writer); out.println(source); out.close(); JavaFileObject file = new JavaSourceFromString(fileName, writer.toString()); //javac的命令内容 Iterable units = Arrays.asList(file); //jar 路径 StringBuilder sb = new StringBuilder(); //遍历目录下所有jar文件,生成classpath String[] filenames = new File(jarPath).list(); for(int i = 0; i < filenames.length; i++) { if(i > 0) sb.append(File.pathSeparatorChar); sb.append(jarPath+filenames[i]); } String classpath = sb.toString(); Iterable options = Arrays.asList("-classpath", classpath, "-d", directoryPath); //编译错误信息 Writer sw = null; JavaCompiler.CompilationTask task = compiler.getTask(sw, null, null, options, null, units); //编译结果 boolean success = task.call(); if (!success) { String failedMsg = sw.toString(); System.out.println("Build Error:" + failedMsg); } else { } return success; }
一定要载入所有该文件所用到的jar包以及外部class文件,不然编译不通过。
2.加载class文件,运行时需要
由于是生成外部class文件,所以我们不能用系统的类加载器,会报错,我们必须自己写一个类加载器,并且重写findClass和loadClass。public class CheckoutClassLoader extends ClassLoader { //根路径(class文件路径) public final static String directoryPath = "xxx\xxxx\"; //特定加载器(Checkout.class 这是我生成class文件中类继承的接口) public CheckoutClassLoader(){ super(Checkout.class.getClassLoader()); } //重写查找class文件 @Override protected Class findClass(final String name) throws ClassNotFoundException { String fullName = name.replace('.', '/'); fullName += ".class"; String path = directoryPath + fullName ; try { FileInputStream fis = new FileInputStream(path); byte[] data = new byte[fis.available()]; fis.read(data); Class res = defineClass(name, data, 0, data.length); fis.close(); return res; } catch(Exception e) { return super.findClass(name); } } }
3.运行
Class c = Class.forName("task.modeOne.type."+one.checkout.getFileName(),true,new CheckoutClassLoader()); checkout = (Checkout) c.newInstance(); //下面可以进行你需要做的事情了
相关文章推荐
- 动态编译java程序实例
- 动态编译JAVA程序
- java 动态编译.java文件,然后动态加载运行
- java动态编译运行代码
- 动态编译JAVA程序
- 在java中利用动态编译实现eval
- 基于JAVA动态编译的高性能对象序列化技术
- 利用javax.tools动态编译执行java代码
- 动态编译Java并通过反射执行方法
- 解决动态编译的时候带bom头的java不编译问题
- 动态编译JAVA程序
- JAVA动态编译简介
- 动态编译JAVA程序(com.sun.tool.javac)
- Java动态编译一个简单的例子(我转载的,但是经过修定,可以在Eclipse下运行)
- java生成java文件并动态编译
- 动态编译Java程序
- 动态编译、运行JAVA程序
- java源码动态生成编译,以及方法调用
- 动态编译JAVA程序
- Java的动态编译