java 动态加载_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 options, Iterable 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 diagnostics是在编译出错时,存放编译错误信息。

第四个参数为编译命令选项,就是javac命令的可选项,这里我们主要使用了-d和-sourcepath这两个选项。

/**

* 编译选项,在编译java文件时,编译程序会自动的去寻找java文件引用的其他的java源文件或者class。 -sourcepath选项就是定义java源文件的查找目录, -classpath选项就是定义class文件的查找目录,-d就是编译文件的输出目录。

*/

Iterable options = Arrays.asList("-d", targetDir, "-sourcepath", sourceDir);

第五个参数为类名称,具体作用没研究清楚。

第六个参数为上面提到的编译单元,就是我们需要编译的java源文件

当我们得到CompilationTask compilationTask编译任务后,我们就可以调用compilationTask.call()方法进行编译工作

// 运行编译任务

compilationTask.call()

下面代码的运行前提条件:

首先需要将附件中的sdl.rar文件解压到F:\亚信工作\SDL文件目录下,sdl目录结构见下图

7e2f80c99b2ea9bb8f522629ce4bbc02.png

b3652e5a532068461d8e86c8b4b7e3ab.png

由于编译这些java文件需要用到两个与sdl相关的jar包,因此在运行下面的代码前,需要将sdl.rar中lib目录下的jar包导入到工程里面来。

源代码如下,直接运行里面的main方法即可。运行后,如果输出:编译成功,则程序正常运行,可以通过查看sdl\classes目录中是否有class文件检查运行结果。

packageutil;importjava.io.File;importjava.io.FileFilter;importjava.io.FilenameFilter;importjava.util.ArrayList;importjava.util.Arrays;importjava.util.List;importjavax.tools.Diagnostic;importjavax.tools.DiagnosticCollector;importjavax.tools.JavaCompiler;importjavax.tools.JavaFileObject;importjavax.tools.StandardJavaFileManager;importjavax.tools.ToolProvider;importjavax.tools.JavaCompiler.CompilationTask;importorg.apache.commons.lang.StringUtils;/***@authorzhengtian

*

* @date 2012-4-17 下午07:24:24*/@SuppressWarnings("all")public classDynamicCompilerUtil {/*** 编译java文件

*

*@paramfilePath

* 文件或者目录(若为目录,自动递归编译)

*@paramsourceDir

* java源文件存放目录

*@paramtargetDir

* 编译后class类文件存放目录

*@paramdiagnostics

* 存放编译过程中的错误信息

*@return*@throwsException*/

public static boolean compiler(String filePath, String sourceDir, String targetDir, DiagnosticCollectordiagnostics)throwsException {//获取编译器实例

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 = newFile(filePath);

List sourceFileList = new ArrayList();

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 options = Arrays.asList("-d", targetDir, "-sourcepath", sourceDir);

CompilationTask compilationTask= compiler.getTask(null, fileManager, diagnostics, options, null, compilationUnits);//运行编译任务

returncompilationTask.call();

}finally{

fileManager.close();

}

}/*** 查找该目录下的所有的java文件

*

*@paramsourceFile

*@paramsourceFileList

*@throwsException*/

private static void getSourceFiles(File sourceFile, List sourceFileList) throwsException {if (sourceFile.exists() && sourceFileList != null) {//文件或者目录必须存在

if (sourceFile.isDirectory()) {//若file对象为目录//得到该目录下以.java结尾的文件或者目录

File[] childrenFiles = sourceFile.listFiles(newFileFilter() {public booleanaccept(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 voidmain(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 diagnostics = new DiagnosticCollector();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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值