JDK6开始提供了动态编译的API,在许多应用场景都可以用得着,如动态加载(修改)服务、高性动态业务逻辑实现(用脚本或模板引擎实现效率满足不了需求)等都非常好用。
API对应的接口都在javax.tools包下面,常用编译方式有基于文本文件、内存字符串等,实际上基于URI的字节流都可以,也就是远程Java源代码也可以。对于常用的已有文件形式的动态编译网上的实例已经非常多,我在这里介绍下动态编译内存中以字符串的形式。
简单的代码流程如下:
//通过系统工具提供者获得动态编译器
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
//获得一个文件管理器,它的功能主要是提供所有文件操作的规则,
//如源代码路径、编译的classpath,class文件目标目录等,其相关属性都提供默认值
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
//获得CompilationTask并调用
//获得CompilationTask方法原型:
getTask(Writer out,
JavaFileManager fileManager,
DiagnosticListener<? super JavaFileObject> diagnosticListener,
Iterable<String> options,
Iterable<String> classes,
Iterable<? extends JavaFileObject> compilationUnits)
//简单调用例子
boolean b = jc.getTask(null, fileManager, null, null, null, compilationUnits).call();
我这里介绍的字符串形式的编译(其它方式也会有相似的具体实现),还需要提供一个FileObject一个实现类,将相应的对象封装作为getTask()的最后一个参数来构建具体的编译Task.
JavaDoc提供的一个FileObject参考实现:
Class JavaSourceFromString
import java.net.URI;
import javax.tools.SimpleJavaFileObject;
public class JavaSourceFromString extends SimpleJavaFileObject {
/**
* 源码
*/
final String code;
/**
* 构造方法:从字符串中构造一个FileObject
* @param name the name of the compilation unit represented by this file object
* @param code the source code for the compilation unit represented by this file object
*/
JavaSourceFromString(String name, String code) {
super(URI.create("string:///" + name.replace('.','/') + Kind.SOURCE.extension),
Kind.SOURCE);
this.code = code;
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return code;
}
}
完整的测试类:
Class TestDyCompile
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileManager.Location;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
import dyclass.Test;
public class TestDyCompile {
/**
*
* @author ZhangXiang
* @param args
* 2011-4-7
*/
public static void main(String[] args) {
StringBuilder classStr = new StringBuilder("package dyclass;public class Foo implements Test{");
classStr.append("public void test(){");
classStr.append("System.out.println(\"Foo2\");}}");
JavaCompiler jc = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = jc.getStandardFileManager(null, null, null);
Location location = StandardLocation.CLASS_OUTPUT;
File[] outputs = new File[]{new File("bin/")};
try {
fileManager.setLocation(location, Arrays.asList(outputs));
} catch (IOException e) {
e.printStackTrace();
}
JavaFileObject jfo = new JavaSourceFromString("dyclass.Foo", classStr.toString());
JavaFileObject[] jfos = new JavaFileObject[]{jfo};
Iterable<? extends JavaFileObject> compilationUnits = Arrays.asList(jfos);
boolean b = jc.getTask(null, fileManager, null, null, null, compilationUnits).call();
if(b){//如果编译成功
try {
Test t = (Test) Class.forName("dyclass.Foo").newInstance();
t.test();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
我在这里的具体业务类为dyclass.Foo,也就是我们需要动态编译的类,为了方便写业务的调用代码,也可以让我们的业务类实现一个接口,然后通过反射获得具体子类强制转换来调用。
Test接口:
public interface Test {
//业务方法签名
void test();
}
另外,在代码中还有这么一段:
Location location = StandardLocation.CLASS_OUTPUT;
File[] outputs = new File[]{new File("bin/")};
try {
fileManager.setLocation(location, Arrays.asList(outputs));
} catch (IOException e) {
e.printStackTrace();
}
这段代码的作用相信大家一看到它就想到它的作用了,前面有说过JavaFileManager 的作用,我在这里设置了CLASS文件的输出目录,意图很简单,我的工程是在Eclipse运行的,项目的目标路径就是项目下的bin目录,如果不设置的话,class文件输出路径即为默认值,也就是直接在项目根路径下,后面直接调用就不能完成了。当然在其它一些应用场景中需要设置为自己需要的目录。
同样的方法可以设置JavaFileManager 其它的我们需要的文件规则属性(可以参照枚举类型StandardLocation),在这里就不一一介绍了。
写东西的时候往往很难静下心来,写得很马虎,多多见谅,有机会大家多一起学习。
分享到:
相关推荐
JDK 6对字符串类(String)进行了优化,如更高效的字符串连接、新的`substring()`实现等,提升了字符串操作的性能。 四、集合框架 Java集合框架是存储和管理对象的重要工具,包括List、Set、Queue和Map接口,以及...
4. **字符串中的switch语句**:允许在`switch`语句中使用字符串对象,增强了选择结构的灵活性。 5. **文件API增强**:NIO.2引入了新的文件系统API,提供了更好的文件操作能力,包括文件路径、链接、遍历目录等功能...
在IT领域,字符串匹配是数据处理中的一个基本问题,它广泛应用于搜索引擎、文本编辑器、病毒扫描等场景。本文将深入探讨传统的字符串匹配方法以及KMP(Knuth-Morris-Pratt)算法,并结合提供的"DemoZwb"压缩包文件,...
本文将详细探讨字符串常量池的存储方式以及与字符串相关的内存管理策略。 首先,字符串常量池位于JVM的永久代(Permanent Generation)中,这个区域在HotSpot VM的实现中由一个叫做StringTable的数据结构维护。...
Nashorn的设计充分利用了JavaScript标准库和Java标准库之间的相似性,允许JavaScript代码直接使用Java的类库,例如JavaScript中的数字可以直接映射为java.lang.Double,字符串映射为java.lang.String,布尔值映射为...
由于Java中的字符串是不可变的,每次使用`+`运算符拼接字符串都会创建新的字符串对象,这在处理大量字符串时会导致内存开销和性能下降。因此,通常推荐使用`StringBuffer`或`StringBuilder`,它们提供了线程安全(`...
此外,还引入了字符串去重复功能,减少了内存消耗。 8. **Java开发工具**:除了基本的JVM和编译器,JDK还提供了如JConsole、JVisualVM等工具,帮助开发者监控和分析Java应用程序的性能。 9. **安全性增强**:JDK ...
例如,`java.lang`包下的`String`类,是处理文本字符串的基础,它的各种方法如`substring()`、`indexOf()`和`trim()`等,为字符串操作提供了便利。`java.util`包中的`ArrayList`和`HashMap`则是常用的数据结构,它们...
3. **字符串in Switch**:允许在switch语句中直接使用字符串作为条件,使得对字符串的处理更方便。 4. **文件系统API**:NIO.2引入了新的文件系统API,提供了更好的文件操作支持,如路径、文件属性和异步I/O操作。 ...
JDK7,即Java SE 7(标准版7),2011年推出,引入了显著的语法改进,例如try-with-resources语句,用于自动关闭资源,增强了switch语句,允许使用字符串作为case标签,以及对多路复用文件I/O的支持。此外,JDK7还...
接着,我们用`Base64.decodeBase64()`方法对编码后的字符串进行解码,还原成原始的字节数组,并再次转换回字符串。 除了Apache Commons Codec,还有其他第三方库如Google的Guava库也提供了Base64操作,但在这里,...
常量池是Java运行时内存的一部分,它存储了编译期间确定的各种常量,包括数值、字符和字符串常量。在类加载时,这些常量会被加载到常量池中。对于字符串常量,Java虚拟机(JVM)有一个专门的字符串常量池,用于存放...
3. **switch语句支持字符串**:switch不再仅限于原始类型和枚举,也可以处理字符串。 4. **try-with-resources**:自动关闭资源,确保资源在使用完毕后被正确释放。 5. **集合接口增强**:例如,`List`、`Map`接口...
2. **枚举**:枚举类型是JDK 6引入的新特性,它提供了一种更安全、更优雅的方式来表示有限的固定值集合,比传统的整数常量或字符串常量更便于管理和使用。 3. **自动装箱与拆箱**:从JDK 6开始,基本数据类型与其...
6. **改进的字符串操作**:`java.lang.String`类在JDK 7u6中增加了几个新方法,如`join()`用于连接字符串数组,`isBlank()`用于判断字符串是否为空或仅包含空白字符,这些方法简化了字符串处理。 7. **编译器优化**...
- **字符串转换为整数的改进**:优化了`Integer.parseInt()`方法,提高了性能。 - **废弃Java EE和CORBA模块**:标志着Java向更轻量级、云原生方向发展。 这些文档将帮助开发者了解每个版本的新功能、API变更以及...
首先,动态生成Java代码通常涉及到字符串或模板引擎。开发者可以使用字符串变量来构造Java源代码,并将其存储为字符串对象。例如,我们可以用StringBuilder或StringBuffer类来拼接代码片段。此外,还可以使用模板...
JDK6的JDBC API进行了优化,提供了更强大的连接池管理,增强了批处理和预编译语句的功能,提升了数据库访问性能。 8. 脚本引擎 JSR 223引入了脚本引擎支持,允许在Java程序中直接执行JavaScript、Ruby等脚本语言...
3. Switch on String:switch语句现在可以接受String对象,使得处理枚举常量和字符串更加方便。 4. try-with-resources:自动资源管理,确保在try块中的资源在退出时会被正确关闭。 5. 其他增强:如动态类型、新的...
3. **泛型(Generics)**:增强了类型安全性,允许在编译时检查类型,如`List<String>`确保列表中只包含字符串。 4. **注解(Annotations)**:元数据的一种形式,用于提供程序信息,不参与程序的执行,但可以被...