Java_动态加载
Java类动态加载(一)——java源文件动态编译为class文件
最近在做java动态加载这方面的工作,起初也遇到了很多困难。网上关于这方便的东西很零散,为了便于日后回过头来再看,于是我将这几天的心得体会总结如下。
什么情况下会需要用java程序动态的编译java源文件,动态的加载java类文件呢?如果很少遇到这样的需求的兄弟们可能不会清楚动态的编译、动态的加载用在一个什么样的场景。下面我将我遇到的场景描述下。
Sdl说明:
为了更好的说明需求,先解释下,我这里的sdl文件是干什么用的。
sdl文件里面主要是定义了一些远程调用接口的相关信息,根据这些信息我们可以自己手动生成java版本的远程调用接口。具体有些什么东西呢?比如说,接口的名称、所在包路径、接口参数、接口用的java bean等等。我们得到这些sdl文件后,可以利用sdl2java.exe或者sdl2java.sh工具将文件编译成java源文件,利用这些源文件就可以进行远程接口调用。
需求说明:
用户只提供一个sdl文件,需要程序能够根据这个sdl文件,提取出所有远程调用接口,让用户在前端输入参数,然后进行远程调用。
实现方案:
用户上传一个sdl文件到工程临时目录,然后程序自动的调用sdl2java.exe或者sdl2java.sh命令将sdl文件动态编译成一系列的java源文件,然后程序动态的将这些java文件编译成class文件,最后再动态加载到项目中。
整个实现方案有三个难点:
- 用java程序调用sdl2java.exe或者sdl2java.sh命令解析sdl文件,生成一系列的java源文件
- 动态编译上述java源文件为class类文件
- 动态加载class类文件
本文讲解的实现方案的前提:
本文主要讲解第二个难点如何实现,因此假设程序已经实现了将sdl文件转换成了一系列的java文件,并存放到服务器中根目录的temp\sdl\src目录中
动态将java文件编译为class文件解决方案:
将temp\sdl\src目录中的java源文件编译成class文件,并存放到temp\sdl\classes目录中。
java中早就提供了用java方式去动态编译java源文件的接口,有关java动态编译的API都在javax.tools包中。本文主要使用jdk1.6以上版本提供的JavaCompiler工具来动态编译java源文件。
我们可以通过ToolProvider类的静态方法getSystemJavaCompiler得到JavaCompiler对象实例。
// 获取编译器实例
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
得到JavaCompiler对象实例后,我们可以调用该工具的getTask(Writer out, JavaFileManager fileManager, DiagnosticListener<? super JavaFileObject> diagnosticListener, Iterable<String> options, Iterable<String> classes, Iterable<? extends JavaFileObject> compilationUnits) 方法获取一个编译任务对象。
CompilationTask compilationTask = compiler.getTask(null, fileManager, diagnostics, options, null, compilationUnits);
该方法的第一个参数为文件输出,这里我们可以不指定,我们采用javac命令的-d参数来指定class文件的生成目录。
第二个参数为文件管理器实例
// 获取标准文件管理器实例
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
该文件管理器实例的作用就是将我们需要动态编译的java源文件转换为getTask需要的编译单元。
// 获取要编译的编译单元
Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromFiles(sourceFileList);
第三个参数DiagnosticCollector<JavaFileObject> diagnostics是在编译出错时,存放编译错误信息。
第四个参数为编译命令选项,就是javac命令的可选项,这里我们主要使用了-d和-sourcepath这两个选项。
/**
* 编译选项,在编译java文件时,编译程序会自动的去寻找java文件引用的其他的java源文件或者class。 -sourcepath选项就是定义java源文件的查找目录, -classpath选项就是定义class文件的查找目录,-d就是编译文件的输出目录。
*/
Iterable<String> options = Arrays.asList("-d", targetDir, "-sourcepath", sourceDir);
第五个参数为类名称,具体作用没研究清楚。
第六个参数为上面提到的编译单元,就是我们需要编译的java源文件
当我们得到CompilationTask compilationTask编译任务后,我们就可以调用compilationTask.call()方法进行编译工作
// 运行编译任务
compilationTask.call()
下面代码的运行前提条件:
首先需要将附件中的sdl.rar文件解压到F:\亚信工作\SDL文件目录下,sdl目录结构见下图
由于编译这些java文件需要用到两个与sdl相关的jar包,因此在运行下面的代码前,需要将sdl.rar中lib目录下的jar包导入到工程里面来。
源代码如下,直接运行里面的main方法即可。运行后,如果输出:编译成功,则程序正常运行,可以通过查看sdl\classes目录中是否有class文件检查运行结果。
package util; import java.io.File;
import java.io.FileFilter;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import javax.tools.JavaCompiler.CompilationTask; import org.apache.commons.lang.StringUtils; /**
* @author zhengtian
*
* @date 2012-4-17 下午07:24:24
*/
@SuppressWarnings("all")
public class DynamicCompilerUtil { /**
* 编译java文件
*
* @param filePath
* 文件或者目录(若为目录,自动递归编译)
* @param sourceDir
* java源文件存放目录
* @param targetDir
* 编译后class类文件存放目录
* @param diagnostics
* 存放编译过程中的错误信息
* @return
* @throws Exception
*/
public static boolean compiler(String filePath, String sourceDir, String targetDir, DiagnosticCollector<JavaFileObject> diagnostics)
throws Exception {
// 获取编译器实例
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
// 获取标准文件管理器实例
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
try {
if (StringUtils.isEmpty(filePath) && StringUtils.isEmpty(sourceDir) && StringUtils.isEmpty(targetDir)) {
return false;
}
// 得到filePath目录下的所有java源文件
File sourceFile = new File(filePath);
List<File> sourceFileList = new ArrayList<File>();
getSourceFiles(sourceFile, sourceFileList);
// 没有java文件,直接返回
if (sourceFileList.size() == 0) {
System.out.println(filePath + "目录下查找不到任何java文件");
return false;
}
// 获取要编译的编译单元
Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromFiles(sourceFileList);
/**
* 编译选项,在编译java文件时,编译程序会自动的去寻找java文件引用的其他的java源文件或者class。 -sourcepath选项就是定义java源文件的查找目录, -classpath选项就是定义class文件的查找目录。
*/
Iterable<String> options = Arrays.asList("-d", targetDir, "-sourcepath", sourceDir);
CompilationTask compilationTask = compiler.getTask(null, fileManager, diagnostics, options, null, compilationUnits);
// 运行编译任务
return compilationTask.call();
} finally {
fileManager.close();
}
} /**
* 查找该目录下的所有的java文件
*
* @param sourceFile
* @param sourceFileList
* @throws Exception
*/
private static void getSourceFiles(File sourceFile, List<File> sourceFileList) throws Exception {
if (sourceFile.exists() && sourceFileList != null) {// 文件或者目录必须存在
if (sourceFile.isDirectory()) {// 若file对象为目录
// 得到该目录下以.java结尾的文件或者目录
File[] childrenFiles = sourceFile.listFiles(new FileFilter() {
public boolean accept(File pathname) {
if (pathname.isDirectory()) {
return true;
} else {
String name = pathname.getName();
return name.endsWith(".java") ? true : false;
}
}
});
// 递归调用
for (File childFile : childrenFiles) {
getSourceFiles(childFile, sourceFileList);
}
} else {// 若file对象为文件
sourceFileList.add(sourceFile);
}
}
} public static void main(String[] args) {
try {
// 编译F:\\亚信工作\\SDL文件\\sdl\\src目录下的所有java文件
String filePath = "F:\\亚信工作\\SDL文件\\sdl\\src";
String sourceDir = "F:\\亚信工作\\SDL文件\\sdl\\src";
String targetDir = "F:\\亚信工作\\SDL文件\\sdl\\classes";
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
boolean compilerResult = compiler(filePath, sourceDir, targetDir, diagnostics);
if (compilerResult) {
System.out.println("编译成功");
} else {
System.out.println("编译失败");
for (Diagnostic diagnostic : diagnostics.getDiagnostics()) {
// System.out.format("%s[line %d column %d]-->%s%n", diagnostic.getKind(), diagnostic.getLineNumber(),
// diagnostic.getColumnNumber(),
// diagnostic.getMessage(null));
System.out.println(diagnostic.getMessage(null));
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
转自:http://www.myexception.cn/program/627493.html
Java_动态加载的更多相关文章
- Java_动态加载类(英文)
It is possible to load and reload classes at runtime in Java, though it is not as straightforward as ...
- js动态加载css和js
之前写了一个工具类点此链接里面含有这段代码,感觉用处挺多,特意提出来 var loadUtil = { /* * 方法说明:[动态加载js文件css文件] * 使用方法:loadUtil.loadjs ...
- geotrellis使用(二十三)动态加载时间序列数据
目录 前言 实现方法 总结 一.前言 今天要介绍的绝对是华丽的干货.比如我们从互联网上下载到了一系列(每天或者月平均等)的MODIS数据,我们怎么能够对比同一区域不同时间的数据情况,采用 ...
- Ext JS 如何动态加载JavaScript创建窗体
JavaScript不需要编译即可运行,这让JavaScript构建的应用程序可以变得很灵活.我们可以根据需要动态从服务器加载JavaScript脚本来创建和控制UI来与用户交互.下面结合Ext JS ...
- Ext动态加载Toolbar
在使用Ext的GridPanel时候,有时候需要面板不用重新加载而去更新Store或者Toolbar,Store的方法有很多,例如官方api给我们提供的Store.load(),Store.reLoa ...
- Android动态加载框架汇总
几种动态加载的比较 1.Tinker 用途:热修复 GitHub地址:https://github.com/Tencent/tinker/ 使用:http://www.jianshu.com/p/f6 ...
- 为不同分辨率单独做样式文件,在页面头部用js判断分辨率后动态加载定义好的样式文件
为不同分辨率单独做样式文件,在页面头部用js判断分辨率后动态加载定义好的样式文件.样式文件命名格式如:forms[_屏幕宽度].css,样式文件中只需重新定义文本框和下拉框的宽度即可. 在包含的头文件 ...
- html中的图像动态加载问题
首先要说明下文档加载完成是什么概念 一个页面http请求访问时,浏览器会将它的html文件内容请求到本地解析,从窗口打开时开始解析这个document,页面初始的html结构和里面的文字等内容加载完成 ...
- 非常郁闷的 .NET中程序集的动态加载
记载这篇文章的原因是我自己遇到了动态加载程序集的问题,而困扰了一天之久. 最终看到了这篇博客:http://www.cnblogs.com/brucebi/archive/2013/05/22/Ass ...
随机推荐
- 64位Ubuntu运行32位程序时报文件不存在(No such file or Directory)的一种解决办法
尝试在64位Ubuntu下面运行32位程序时, 一直说 文件不存在(No such file or directory), 我只想说++. 你tm说个文件格式不正确不就好了? 非得说个文件不存在! 真 ...
- 第一个java程序hello world
首先需要配置环境,没配置的请参考:详细配置教程:http://www.cnblogs.com/qq1871707128/p/6047232.html 切入主题: java基础首先得了解基本的dos命令 ...
- Linux学习笔记(8)Linux常用命令之网络命令
(1)write write命令用于给指定用户发信息,以Ctrl+D保存结束,所在路径为/usr/bin/write,其语法格式为: write [用户名] 注:只能给在线用户发送. 例:新建ws用户 ...
- js高级技巧之高级定时器
实际上,浏览器负责进行排序,指派某段代码在某个时间点运行的优先级. 可以吧js想象成在时间线上运行的. JavaScript中没有任何代码是立刻执行的,但一旦进程空闲则尽快执行. 1.重复的定时器: ...
- JavaScript设计模式——单体模式
一:单体模式简介: 是什么:将代码组织为一个逻辑单元,这个单元中的代码通过单一的变量进行访问.只要单体对象存在一份实例,就可以确信自己的所有代码使用的是同样的全局资源. 用途:1.用来划分命名空间,减 ...
- cf429B dp递推
Description Summer is coming! It's time for Iahub and Iahubina to work out, as they both want to loo ...
- PHP学习之字符串
1.字符串变量用于存储并处理文本: 2.字符串变量用于包含有字符的值,在创建字符串后,就可以对它进行操作了,可以直接在函数中使用字符串,或把它存储在变量中: <?php $txt="H ...
- loopback 02
数据库连接操作,以mongodb为例 安装loopback-connector-mongodb 修改datasources.json //例子 { "db": { "na ...
- jade学习01
编写 简单例子 doctype html html head title learn jade body h1 learn jade 常用命令 编译: jade index.jade //默认编译成压 ...
- 车销 商场 批发零售无线POS开单 智能POS开单打印 开单APP应用-云POS扫描打印一体方案
PDA数据采集器,是一款移动手持开单设备,它通过WIFI和GPRS连接并访问电脑,从进销存软件中读取数据,实现移动开单,打破电脑开单模式. 它自带扫描器,可直接扫描条码来查找产品,且功能强大.操作简单 ...