使用javac编译java源文件_Java种动态加载(一)——java源文件动态编译为class文件...

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目录结构见下图

2218398255.jpg

2218398256.jpg

由于编译这些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 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 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);

// 运行编译任务

return compilationTask.call();

} finally {

fileManager.close();

}

}

/**

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

*

* @param sourceFile

* @param sourceFileList

* @throws Exception

*/

private static void getSourceFiles(File sourceFile, List 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 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.mzph.cn/news/338772.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

gradle构建多模块项目_Gradle入门:创建多项目构建

gradle构建多模块项目尽管我们可以仅使用一个模块来创建一个运行中的应用程序,但有时将我们的应用程序划分为多个较小的模块是比较明智​​的。 因为这是一个相当普遍的用例,所以每个自重的构建工具都必须支持它,Gradle也不例外。 如果Gradl…

python第一条入门程序_Python语言函数代码的执行流程

https://www.xin3721.com/eschool/pythonxin3721/Python语言函数代码的执行流程,为了保证函数的定义先于其首次调用时执行,我们需要知道中语句的执行顺序。执行总是从程序的第一行代码开始的,从上到下,从左到右,按顺序…

嵌入式C中,全局变量滥用的后果竟如此严重?

说起全局变量,就不得不提到“全局变量,局部变量,静态全局变量,静态局部变量”,这些都是编程语言中的基本概念。变量分为局部与全局,局部变量又可称之为内部变量。由某对象或某个函数所创建的变量通常都是局…

php登陆页面修改密码的功能,使用bootstrap创建登录注册页面并实现表单验证功能...

本篇文章给大家介绍一下使用bootstrap创建登录注册页面并实现单验证功能的方法。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。用bootstrap做登入注册页面,使用validate做表单验证技术:bootstrap,f…

java压缩文件读取_用Java读取/写入压缩和非压缩文件

java压缩文件读取这篇文章的主要原因是尝试不要重复自己( DRY ),因为通常,我会遇到递归的需求,即读写压缩的和非压缩的文件(主要是JSON和CSV)。 首先让我们看看如何读取文本文件。 注意我正在使…

vue router 参数_Vue.js项目开发技术解析

Vue.js项目开发技术解析一、Vue.js实例在一个Vue.js工程中,用于显示内容最基层的实例称之为根实例。通过该实例可以进行页面或组件的更新和显示。对于项目本身而言,无论是什么样的页面,都要基于该根实例进行显示。1.1、何为构造器对于Vue.js项…

deepin nginx连接php,利用docker运行nginx加上本机的php-fpm。访问html文件正常,但是访问php文件就报错404...

最近在虚拟机弄了一个deepin系统。打算使用docker加载nginx/1.17.6加在官方下载的PHP7.4.1(这个是本地编译的)。出现了以下的问题:1)访问html文件正常,但是访问php文件报404。根据网上的文章尝试:1)php运行不成功。在PHP的cli模式下是正常的。…

C语言中面向对象编程

C语言中面相对象的编程面向对象的重要思想就是数据隐藏,在面向对象语言中,对象可以包含私有变量。这样我们可以说他们具有内部状态,这些内部状态对其他对象是透明的。全局变量可以通过设置变量作用域来模拟私有变量(甚至友元对象)…

jmeter测试客户端_如何在JMeter中执行客户端Web性能测试?

jmeter测试客户端在本文中,我们将看到如何使用Jmeter插件进行客户端性能测试。 我将使用jmeter webdriver插件。 在开始本主题之前,请阅读我以前的文章中有关客户端性能测试的一些基本信息。 因此,让我们开始吧: 安装 通过这篇文…

java编译程序的基本命令是什么,【填空题】Java中编译java 程序的命令是 1 ,执行java程序的命令是 java 。...

【填空题】Java中编译java 程序的命令是 1 ,执行java程序的命令是 java 。更多相关问题[多选] 下列骨折中属于关节外骨折的是()。[多选] 骨折早期局部的并发症包括()。[多选] 典型中1/3段锁骨骨折的内侧端移位方向是()。[多选] 骨关节结核的诊断要点包括()。[多选] 骨痂形成的方…

python range 小数_python中如何表示一个无限循环小数?(不用分数的形式)python,使用range语...

python中如何表示一个无限循环小数?(不用分数的形式) python,使用range语www.zhiqu.org 时间: 2020-12-07只能用分数或者你自己设计一个对象,保留指定长度的有效位range()只能产生int无法产生float但是可以曲线救国for i in range(0,10,1…

C语言实现面向接口编程

面向接口编程话不多说,先上一个面向接口编程的Demo:参考demo:1#include 2#include 34/********************************************5 * Note: 共用接口的定义 6 * author:bug菌 7 *******************************************/8struct Interface {9 …

java ee用户登录_EE Servlet 3:使用会话和过滤器开发用户登录

java ee用户登录我在上一篇文章中介绍了Application类,您可以在其中设置后端服务。 我添加的一个示例服务是UserService 。 该服务将加载包含用户名和密码集的Java用户属性文件; 稍后将用于对用户进行身份验证以登录到Web应用程序。 现在,我将…

php 静态方法 call,php的 __callStatic 函数

现在很多框架中调用方法都是Foo::bar()这种格式的,但是他们真的是静态方法吗?这种情况在 larave 中尤其常见,但是开发过程中很明显这些有一部分不是静态的,比如你使用一个模型User,那么你每次实例化出来他都是一个全新的,互不影响,这里就用到了一个魔术方法__callStatic举个栗…

python高手之路第三版_《Python高手之路(第3版)》——1.3 版本编号-阿里云开发者社区...

本节书摘来自异步社区《Python高手之路(第3版)》一书中的第1章,第1.3节,作者[法]Julien Danjou,王飞龙 译,更多章节内容可以访问云栖社区“异步社区”公众号查看。1.3 版本编号可能你已经有所了解&#xf…

「C语言」指针数组 数组指针 指针函数 函数指针

相信很多人和我一样,有着这样的恐惧,那就是这四个玩意怎么也分不清,这都是啥啥啥啥呢?今天我们来具体分析一下。其实要具体了解这四个概念,只需要了解符号优先级,并关注最后两字,你就成功了一半…

spring 组件扫描_避免不必要的Spring配置组件扫描

spring 组件扫描我在堆栈溢出中遇到了一个有趣的问题。 Brett Ryan有问题,Spring Security配置被初始化了两次。 当我查看他的代码时,我发现了问题所在。 让我展示显示代码。 他有相当标准的Spring应用程序(不使用Spring Boot)。…

php滚动到指定位置,JQuery插件:ScrollTo平滑滚动到页面指定位置

ScrollTo是一款基于jQuery的滚动插件,当点击页面的链接时,可以平滑地滚动到页面指定的位置。适用在一些页面内容比较多,页面长度有好几屏的场合,本文以DEMO为示例讲解该插件的应用。查看演示DEMO使用方法1-准备jQuery库…

聚集索引和非聚集索引的区别_武汉无疫情小区居民可在小区内非聚集性个人活动...

3月18日,武汉市新冠肺炎疫情防控指挥部社区疫情防控组发布《关于无疫情小区、村(队)调整管控措施的意见》(下简称《意见》)指出,全市认定的无疫情小区、村(队)可对管控措施作有序调整。 被认定为无疫情小区的,允许居民分批、分时段、分楼栋&a…

C语言 | 指向指针的指针排序

C语言实现用指向指针的指针的方法对5个字符串排序并输出。 解题思路:读者看着道题的时候,首先要知道什么时指针,指向指针的指针应该怎么用,一般在开发中不这样用,读者要看明白,这个很锻炼思维的。C语言源代…