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;
    }
}
时间: 2024-10-07 05:28:37

Java--自定义Class并且在内存中编译,加载,实例化的相关文章

Java中动态加载jar文件和class文件

概述 诸如tomcat这样的服务器,在启动的时候会加载应用程序中lib目录下的jar文件以及classes目录下的class文件,另外像spring这类框架,也可以根据指定的路径扫描并加载指定的类文件,这个技术可以实现一个容器,容纳各类不同的子应用. Java类由于需要加载和编译字节码,动态加载class文件较为麻烦,不像C加载动态链接库只要一个文件名就可以搞定,但JDK仍提供了一整套方法来动态加载jar文件和class文件. 动态加载jar文件 // 系统类库路径 File libPath =

3、在Ajax于Java的交互过程中,加入加载图片

1.在Ajax于Java的交互过程中,加入加载图片 1.1 html代码 <body> <button id="mybtn">点击</button> <div id="myAjax"> <img src="./img/load2.jpg" id="myimg"> </div> <!-- 模拟等待的图片 --> </body> 1.2

java web.xml listener servlet 和filter的加载顺序

在项目中总会遇到一些关于加载的优先级问题,近期也同样遇到过类似的,所以自己查找资料总结了下,下面有些是转载其他人的,毕竟人家写的不错,自己也就不重复造轮子了,只是略加点了自己的修饰. 首先可以肯定的是,加载顺序与它们在 web.xml 文件中的先后顺序无关.即不会因为 filter 写在 listener 的前面而会先加载 filter. 最终得出的结论是:listener -> filter -> servlet 同时还存在着这样一种配置节:context-param,它用于向 Servle

【Android自定义View实战】之仿百度加载动画,一种优雅的Loading方式

转载请注明出处:http://blog.csdn.net/linglongxin24/article/details/53470872 本文出自[DylanAndroid的博客] Android自定义View实战之仿百度加载动画一种优雅的Loading方式 第一个仿百度加载动画用ObjectAnimator属性动画操作ImageView的属性方法实现 第二个仿百度加载动画第二种实现方式用ValueAnimator原生的ondraw方法实现 第三个扔球动画-水平旋转动画 第四个扔球动画-垂直旋转动

Android 自定义 ListView 上下拉动&ldquo;刷新最新&rdquo;和&ldquo;加载更多&rdquo;歌曲列表

本文内容 环境 测试数据 项目结构 演示 参考资料 本文演示,上拉刷新最新的歌曲列表,和下拉加载更多的歌曲列表.所谓"刷新最新"和"加载更多"是指日期.演示代码太多,点击此处下载,自己调试一下. 下载 Demo 环境 Windows 2008 R2 64 位 Eclipse ADT V22.6.2,Android 4.4.3 SAMSUNG GT-I9008L,Android OS 2.2.2 测试数据 本演示的歌曲信息,共有 20 条,包括歌手名.歌曲名.时长.缩

Java基础6:代码块与代码加载顺序

Java基础6:代码块与代码加载顺序 代码块:用{}包围的代码 java中的代码块按其位置划分为四种: 局部代码块 位置:局部位置(方法内部) 作用:限定变量的生命周期,尽早释放,节约内存 调用:调用其所在的方法时执行 public class 局部代码块 {@Testpublic void test (){   B b = new B();   b.go();}}class B {   B(){}   public void go() {       //方法中的局部代码块,一般进行一次性地调

cocos2dx lua中异步加载网络图片,可用于显示微信头像

最近在做一个棋牌项目,脚本语言用的lua,登录需要使用微信登录,用户头像用微信账户的头像,微信接口返回的头像是一个url,那么遇到的一个问题就是如何在lua中异步加载这个头像,先在引擎源码里找了下可能会提供这个功能的地方,发现好像没有提供类似功能,那么只能自己动手写.所以我在ImageView这个类里面添加了一个成员方法,其实可以不写在ImageView里,而且我觉得非必需情况下还是不要修改引擎源码的好,因为如果源码改动比较多的话,将来引擎版本升级会比较麻烦.我写在ImageView里纯粹是想偷

【内存优化】加载一张图像资源到底占据多少内存

0.内容概览 1. 简介 2. 问题 3. 概念描述 4. 具体分析 5. 总结 6. 参考文档 1.简介 Android中经常要通过ImageView进行图片资源显示.在加载图片时,首先要考虑的两个因素就是体验问题和性能问题. 其中,体验问题是指图片显示的是否正确(例如Universal-Image-Loader在适配Adapter图片资源时会导致图片显示错位),分辨率是否合适等.而体验问题主要是指图片加载速度,以及更加重要的图片加载的内存占用问题.本文重点介绍ImageView加载图片中的内

Chromium中网页加载进度条研究

1.     Shell.java中有成员变量:mProgressDrawable. 该成员变量在方法:onFinishInflate中被初始化. 在该类中有方法:onLoadProgressChanged,该方法中对进度条的值进行改变,并且对刷新完成事件进行反馈. 2.     上面的这个方法是在cc文件中被调用的. 上面方法对应的cc方法是shell_android.cc文件中的LoadProgressChanged方法. voidShell::LoadProgressChanged(Web