一.动态编译
在某些情况下,我们需要动态生成java代码,通过动态编译,然后执行代码。JAVA API提供了相应的工具(JavaCompiler)来实现动态编译。
//获取JavaCompiler JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
//获取java文件管理类 StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null);
//获取java文件对象迭代器 Iterable<? extends JavaFileObject> it = manager.getJavaFileObjects(files);
//设置编译参数 ArrayList<String> ops = new ArrayList<String>(); ops.add("-Xlint:unchecked");
//可以指定源文件或目标文件的JDK版本 ops.add("-source")/ops.add("-target");; ops.add("1.5");
//获取编译任务:第一个null,用于输出错误的流,默认是System.err;第二个null,diagnosticListener: 编译器的默认行为 ;第三个null,classes,参与编译的class,为null时表示全部
//JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, ops, null, it);
//执行编译任务 task.call();
动态编译几个关键类
类名 |
作用 |
创建方式 |
JavaCompiler |
动态编译的入口,也是基础类 |
ToolProvider.getSystemJavaCompiler() |
StandardJavaFileManager |
java文件管理类 |
compiler.getStandardFileManager |
Iterable |
文件对象迭代器 |
manager.getJavaFileObjects(files) |
ArrayList<String> |
编译参数 |
new ArrayList<String>() |
踩坑记
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 这句代码在IDEA中运行正常,然后打成jar包在桌面双击运行时得到的是null
问题解决:
查看ToolProvider源码可知,是因为找不到tools.jar---》将tools.jar复制到jre/lib下
二.动态加载
JAVA中的类加载:双亲委派模式
URL url = new URL("file:" + CLASS_PATH);
ClassLoader classLoader = new URLClassLoader(new URL[]{url});
Class<?> cls = classLoader.loadClass(name);
Method method = cls.getDeclaredMethod(String methodName)
Object obj = cls.newInstance();
Object result = method.invoke(Object obj, Object[] params);
class.forName()和classLoader区别:
//Class.forName(String className) 这是1.8的源码
public static Class<?> forName(String className) throws ClassNotFoundException {
Class<?> caller = Reflection.getCallerClass();
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
//注意第二个参数,是指Class被loading后是不是必须被初始化。 不初始化就是不执行static的代码即静态代码 class.forName()前者除了将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块。
而classLoader只干一件事情,就是将.class文件加载到jvm中。
扩展:TOMCAT中的类加载架构
1、JAVA_HOME:设置为JDK的安装目录,如D:\java\jdk1.8。一些依赖JAVA的软件通过该环境变量来获得JDK的安装目录,如大家都知道的Eclipse、Tomcat等。
2、PATH:在已有值最后加上 ;%JAVA_HOME%\bin ,其中;是分隔符,用于分开每个目录,其中%JAVA_HOME%则是引用上面的JAVA_HOME环境变量的值,可以直接用JDK的安装目录代替,下面的CLASSPATH类似。执行路径(也就是PATH环境变量的值)是只操作系统搜索本地可执行文件的目录列表,在shell中执行命令时,它会在执行路径中查找所对应的程序。所以在PATH中加入了%JAVA_HOME%\bin目录后,在shell中就可以执行javac或者java命令。
3、CLASSPATH:设置值为.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar; 注意其中的标点符号,其中第一个点“.”表示当前目录,而分号“;”则是分隔符。 这个CLASSPATH为JVM加载类的目录,比如HelloWorld.java中有import其他类,用javac编译时它会在CLASSPATH中去找你所import的类,如果找不到则出现编译错误。 另外在使用java时使用-classpath 参数可以指定要加载类的目录,比如命令行执行:C:/UsersJakey>java -classpath D:\temp HelloWorld ,这样就可以当命令行在C盘时执行D盘目录中的JAVA类。