Java--自定义Class并且在内存中编译,加载,实例化
本文的目的:
使用者在程序运行期间,可以动态的写Java Class,不需要生成任何.Class文件就可以完全在内存中编译,加载,实例化。
1、需要用到的组件介绍
1)JavaCompiler:用于编译Java Code。
2)CharSequenceJavaFileObject:用于保存Java Code,提供方法给JavaCompiler获取String形式的Java Code。
3)ClassFileManager:用于JavaCompiler将编译好后的Class文件保存在指定对象中。
4)JavaClassObject:ClassFileManager告诉JavaCompiler需要将Class文件保存在JavaClassObject中,但是由JavaClassObject来决定最终以byte流来保存数据。
5)DynamicClassLoader:自定义类加载器,用于加载最后的二进制Class
2、源码展现:
CharSequenceJavaFileObject.java
- package com.ths.platform.framework.dynamic;
- import javax.tools.SimpleJavaFileObject;
- import java.net.URI;
- /**
- * 用于将java源码保存在content属性中
- */
- public class CharSequenceJavaFileObject extends SimpleJavaFileObject {
- /**
- * 保存java code
- */
- private String content;
- /**
- * 调用父类构造器,并设置content
- * @param className
- * @param content
- */
- public CharSequenceJavaFileObject(String className, String content){
- super(URI.create("string:///" + className.replace('.', '/')
- + Kind.SOURCE.extension), Kind.SOURCE);
- this.content = content;
- }
- /**
- * 实现getCharContent,使得JavaCompiler可以从content获取java源码
- * @param ignoreEncodingErrors
- * @return
- */
- @Override
- public String getCharContent(boolean ignoreEncodingErrors) {
- return content;
- }
- }
ClassFileManager.java
- package com.ths.platform.framework.dynamic;
- import java.io.IOException;
- import javax.tools.*;
- /**
- * 类文件管理器
- * 用于JavaCompiler将编译好后的class,保存到jclassObject中
- */
- public class ClassFileManager extends ForwardingJavaFileManager {
- /**
- * 保存编译后Class文件的对象
- */
- private JavaClassObject jclassObject;
- /**
- * 调用父类构造器
- * @param standardManager
- */
- public ClassFileManager(StandardJavaFileManager standardManager) {
- super(standardManager);
- }
- /**
- * 将JavaFileObject对象的引用交给JavaCompiler,让它将编译好后的Class文件装载进来
- * @param location
- * @param className
- * @param kind
- * @param sibling
- * @return
- * @throws IOException
- */
- @Override
- public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling)
- throws IOException {
- if (jclassObject == null)
- jclassObject = new JavaClassObject(className, kind);
- return jclassObject;
- }
- public JavaClassObject getJavaClassObject() {
- return jclassObject;
- }
- }
JavaClassObject.java
- package com.ths.platform.framework.dynamic;
- import javax.tools.SimpleJavaFileObject;
- import java.io.ByteArrayOutputStream;
- import java.io.IOException;
- import java.io.OutputStream;
- import java.net.URI;
- /**
- * 将输出流交给JavaCompiler,最后JavaCompiler将编译后的class文件写入输出流中
- */
- public class JavaClassObject extends SimpleJavaFileObject {
- /**
- * 定义一个输出流,用于装载JavaCompiler编译后的Class文件
- */
- protected final ByteArrayOutputStream bos = new ByteArrayOutputStream();
- /**
- * 调用父类构造器
- * @param name
- * @param kind
- */
- public JavaClassObject(String name, Kind kind) {
- super(URI.create("string:///" + name.replace('.', '/') + kind.extension), kind);
- }
- /**
- * 获取输出流为byte[]数组
- * @return
- */
- public byte[] getBytes() {
- return bos.toByteArray();
- }
- /**
- * 重写openOutputStream,将我们的输出流交给JavaCompiler,让它将编译好的Class装载进来
- * @return
- * @throws IOException
- */
- @Override
- public OutputStream openOutputStream() throws IOException {
- return bos;
- }
- /**
- * 重写finalize方法,在对象被回收时关闭输出流
- * @throws Throwable
- */
- @Override
- protected void finalize() throws Throwable {
- super.finalize();
- bos.close();
- }
- }
DynamicEngine.java(职责:使用JavaCompiler编译Class,并且使用DynamicClassLoader加载Class)
- package com.ths.platform.framework.dynamic;
- import java.io.File;
- import java.net.URL;
- import java.net.URLClassLoader;
- import java.util.ArrayList;
- import java.util.List;
- import javax.tools.Diagnostic;
- import javax.tools.DiagnosticCollector;
- import javax.tools.JavaCompiler;
- import javax.tools.JavaFileObject;
- import javax.tools.ToolProvider;
- /**
- * 在Java SE6中最好的方法是使用StandardJavaFileManager类。
- * 这个类可以很好地控制输入、输出,并且可以通过DiagnosticListener得到诊断信息,
- * 而DiagnosticCollector类就是listener的实现。
- * 使用StandardJavaFileManager需要两步。
- * 首先建立一个DiagnosticCollector实例以及通过JavaCompiler的getStandardFileManager()方法得到一个StandardFileManager对象。
- * 最后通过CompilationTask中的call方法编译源程序。
- */
- public class DynamicEngine {
- //单例
- private static DynamicEngine ourInstance = new DynamicEngine();
- public static DynamicEngine getInstance() {
- return ourInstance;
- }
- private URLClassLoader parentClassLoader;
- private String classpath;
- private DynamicEngine() {
- //获取类加载器
- this.parentClassLoader = (URLClassLoader) this.getClass().getClassLoader();
- //创建classpath
- this.buildClassPath();
- }
- /**
- * @MethodName : 创建classpath
- */
- private void buildClassPath() {
- this.classpath = null;
- StringBuilder sb = new StringBuilder();
- for (URL url : this.parentClassLoader.getURLs()) {
- String p = url.getFile();
- sb.append(p).append(File.pathSeparator);
- }
- this.classpath = sb.toString();
- }
- /**
- * @MethodName : 编译java代码到Object
- * @Description : TODO
- * @param fullClassName 类名
- * @param javaCode 类代码
- * @return Object
- * @throws Exception
- */
- public Object javaCodeToObject(String fullClassName, String javaCode) throws Exception {
- long start = System.currentTimeMillis(); //记录开始编译时间
- Object instance = null;
- //获取系统编译器
- JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
- // 建立DiagnosticCollector对象
- DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
- // 建立用于保存被编译文件名的对象
- // 每个文件被保存在一个从JavaFileObject继承的类中
- ClassFileManager fileManager = new ClassFileManager(compiler.getStandardFileManager(diagnostics, null, null));
- List<JavaFileObject> jfiles = new ArrayList<JavaFileObject>();
- jfiles.add(new CharSequenceJavaFileObject(fullClassName, javaCode));
- //使用编译选项可以改变默认编译行为。编译选项是一个元素为String类型的Iterable集合
- List<String> options = new ArrayList<String>();
- options.add("-encoding");
- options.add("UTF-8");
- options.add("-classpath");
- options.add(this.classpath);
- JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, options, null, jfiles);
- // 编译源程序
- boolean success = task.call();
- if (success) {
- //如果编译成功,用类加载器加载该类
- JavaClassObject jco = fileManager.getJavaClassObject();
- DynamicClassLoader dynamicClassLoader = new DynamicClassLoader(this.parentClassLoader);
- Class clazz = dynamicClassLoader.loadClass(fullClassName,jco);
- instance = clazz.newInstance();
- } else {
- //如果想得到具体的编译错误,可以对Diagnostics进行扫描
- String error = "";
- for (Diagnostic diagnostic : diagnostics.getDiagnostics()) {
- error += compilePrint(diagnostic);
- }
- }
- long end = System.currentTimeMillis();
- System.out.println("javaCodeToObject use:"+(end-start)+"ms");
- return instance;
- }
- /**
- * @MethodName : compilePrint
- * @Description : 输出编译错误信息
- * @param diagnostic
- * @return
- */
- private String compilePrint(Diagnostic diagnostic) {
- System.out.println("Code:" + diagnostic.getCode());
- System.out.println("Kind:" + diagnostic.getKind());
- System.out.println("Position:" + diagnostic.getPosition());
- System.out.println("Start Position:" + diagnostic.getStartPosition());
- System.out.println("End Position:" + diagnostic.getEndPosition());
- System.out.println("Source:" + diagnostic.getSource());
- System.out.println("Message:" + diagnostic.getMessage(null));
- System.out.println("LineNumber:" + diagnostic.getLineNumber());
- System.out.println("ColumnNumber:" + diagnostic.getColumnNumber());
- StringBuffer res = new StringBuffer();
- res.append("Code:[" + diagnostic.getCode() + "]\n");
- res.append("Kind:[" + diagnostic.getKind() + "]\n");
- res.append("Position:[" + diagnostic.getPosition() + "]\n");
- res.append("Start Position:[" + diagnostic.getStartPosition() + "]\n");
- res.append("End Position:[" + diagnostic.getEndPosition() + "]\n");
- res.append("Source:[" + diagnostic.getSource() + "]\n");
- res.append("Message:[" + diagnostic.getMessage(null) + "]\n");
- res.append("LineNumber:[" + diagnostic.getLineNumber() + "]\n");
- res.append("ColumnNumber:[" + diagnostic.getColumnNumber() + "]\n");
- return res.toString();
- }
- }
DynamicClassLoader.java
- package com.ths.platform.framework.dynamic;
- import java.net.URLClassLoader;
- import java.net.URL;
- /**
- * 自定义类加载器
- */
- public class DynamicClassLoader extends URLClassLoader {
- public DynamicClassLoader(ClassLoader parent) {
- super(new URL[0], parent);
- }
- public Class findClassByClassName(String className) throws ClassNotFoundException {
- return this.findClass(className);
- }
- public Class loadClass(String fullName, JavaClassObject jco) {
- byte[] classData = jco.getBytes();
- return this.defineClass(fullName, classData, 0, classData.length);
- }
- }
DynaCompTest.java(测试类,从myclass文件中读出源码并在内存中编译)
- package com.ths.platform.framework.dynamic;
- import sun.misc.IOUtils;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.InputStream;
- public class DynaCompTest
- {
- public static void main(String[] args) throws Exception {
- String fullName = "com.seeyon.proxy.MyClass";
- File file = new File("/Users/yangyu/Downloads/myclass");
- InputStream in = new FileInputStream(file);
- byte[] bytes = IOUtils.readFully(in, -1, false);
- String src = new String(bytes);
- in.close();
- System.out.println(src);
- DynamicEngine de = DynamicEngine.getInstance();
- Object instance = de.javaCodeToObject(fullName,src.toString());
- System.out.println(instance);
- }
- }
/Users/yangyu/Downloads/myclass文件(这里使用文件,实际也可以在程序中直接拼凑String)
- package com.seeyon.proxy;
- public class MyClass {
- public String say(String str){
- return "hello"+str;
- }
- }
Java--自定义Class并且在内存中编译,加载,实例化的更多相关文章
- Qt 5.2中编译加载MySQL数据库驱动问题的总结
背景: 本科毕业设计涉及图形界面与数据库查询.选择使用Qt实现图形界面编程,使用MySQL构建数据库.之前安装了Qt 5.2,后来又安装了MySQL Server 5.6 (FULL完全安装).接着就 ...
- 【Java面试题】解释内存中的栈(stack)、堆(heap)和静态存储区的用法
Java面试题:解释内存中的栈(stack).堆(heap)和静态存储区的用法 堆区: 专门用来保存对象的实例(new 创建的对象和数组),实际上也只是保存对象实例的属性值,属性的类型和对象本身的类型 ...
- Java 错误:找不到或无法加载主类(源文件中含有包名 package)
1. 问题定位 编译(javac)和执行(java)java 程序时,出现这种类型的错误:找不到或无法加载主类: 首先排除是否是环境变量配置不当造成的问题,只要保证,命令行界面能够识别 javac/j ...
- Tomcat启动时加载数据到缓存---web.xml中listener加载顺序(例如顺序:1、初始化spring容器,2、初始化线程池,3、加载业务代码,将数据库中数据加载到内存中)
最近公司要做功能迁移,原来的后台使用的Netty,现在要迁移到在uap上,也就是说所有后台的代码不能通过netty写的加载顺序加载了. 问题就来了,怎样让迁移到tomcat的代码按照原来的加载顺序进行 ...
- 某APK中使用了动态注册BroadcastReceiver,Launcher中动态加载此APK出现java.lang.SecurityException异常的解决方法
在某APK中,通过如下方法动态注册了一个BroadcastReceiver,代码参考如下: @Override protected void onAttachedToWindow() { super. ...
- 运行Java cmd程序 找不到或无法加载主类怎么解决
//这个问题原因有以下几种,但是和环境变量并没有太大的关系 //能够执行java 和 javac 就证明你的环境变量已经配置好了,其实 classpath 可以不配置 //假如有如下文件:H:\cod ...
- 在MVC应用程序中动态加载PartialView
原文:在MVC应用程序中动态加载PartialView 有时候,我们不太想把PartialView直接Render在Html上,而是使用jQuery来动态加载,或是某一个事件来加载.为了演示与做好这个 ...
- cocos2dx lua中异步加载网络图片,可用于显示微信头像
最近在做一个棋牌项目,脚本语言用的lua,登录需要使用微信登录,用户头像用微信账户的头像,微信接口返回的头像是一个url,那么遇到的一个问题就是如何在lua中异步加载这个头像,先在引擎源码里找了下可能 ...
- 在mybatis 中批量加载mapper.xml
可以直接加载一个包文件名,将这个包里的所有*mapper.xml文件加载进来. 指定mapper接口的包名,mybatis自动扫描包下边所有mapper接口进行加载: 必须按一定的标准:即xml文件和 ...
随机推荐
- Aspose.Words 16.8 破解版、添加自定义HTML导出Jpeg压缩质量配置
0x01 Aspose.Words 介绍Aspose.Words是一个商业.NET类库,可以使得应用程序处理大量的文件任务.Aspose.Words支持Doc,Docx,RTF,HTML,OpenDo ...
- jquery中bind()绑定多个事件
bind()绑定事件 $(selector).bind(event,data,function): 参数event为事件名称(如"click,mouseover....."),da ...
- ESP8266刷AT固件与nodemcu固件
这回是使用的这一款 因为这款默认的是支持AT指令的固件,,所以我们就刷nodemcu的 先看接线 GPIO0 默认是工作模式(不接线).如果接了低电平就是下载模式(给模块刷固件!!)所以接低电平.CH ...
- 无法启动WP Emulator
记得以前Vware不能运行设置的东西了吗?http://www.cnblogs.com/dunitian/p/4480750.html 如果不清楚可以参考上面的链接 重启的时候选择第二项 重新打开就o ...
- OpenCASCADE General Transformation
OpenCASCADE General Transformation eryar@163.com Abstract. OpenCASCADE provides a general transforma ...
- php语句
判断变量的方法: 例: $a=""; var_dump(empty($a)); 输出的结果为true 若$a=10; var_dump(empty($a)); 输出falst 若没 ...
- 配置 L2 Population - 每天5分钟玩转 OpenStack(114)
前面我们学习了L2 Population 的原理,今天讨论如何在 Neutron 中配置和启用此特性. 目前 L2 Population 支持 VXLAN with Linux bridge 和 VX ...
- ERROR 1819 (HY000): Your password does not satisfy the current policy requirements
为了加强安全性,MySQL5.7为root用户随机生成了一个密码,在error log中,关于error log的位置,如果安装的是RPM包,则默认是/var/log/mysqld.log. 一般可通 ...
- ModelState.IsValid总为false原因
总结在开发中遇到的一个问题 ModelState.IsValid 一直是false 且在局部变量中,没有发现有问题啊,Model非常正常有木有,可是为什么 ModelState.IsValid 总是f ...
- [Keras] mnist with cnn
典型的卷积神经网络. Keras傻瓜式读取数据:自动下载,自动解压,自动加载. # X_train: array([[[[ 0., 0., 0., ..., 0., 0., 0.], [ 0., 0. ...