`
oaklet
  • 浏览: 107642 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Java动态编译笔记

    博客分类:
  • java
阅读更多
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.ToolProvider;

import org.apache.log4j.Logger;

/**
 * 动态重新加载Class <br>
 * Java内置的ClassLoader总会在加载一个Class之前检查这个Class是否已经被加载过 <br>
 * 已经被加载过的Class不会加载第二次 <br>
 * 因此要想重新加载Class,我们需要实现自己的ClassLoader <br>
 * 另外一个问题是,每个被加载的Class都需要被链接(link), <br>
 * 这是通过执行ClassLoader.resolve()来实现的,这个方法是 final的,无法重写。 <br>
 * ClassLoader.resolve()方法不允许一个ClassLoader实例link一个Class两次, <br>
 * 因此,当需要重新加载一个 Class的时候,需要重新New一个自己的ClassLoader实例。 <br>
 * 一个Class不能被一个ClassLoader实例加载两次,但是可以被不同的ClassLoader实例加载, <br>
 * 这会带来新的问题 <br>
 * 在一个Java应用中,Class是根据它的全名(包名+类名)和加载它的 ClassLoader来唯一标识的, <br>
 * 不同的ClassLoader载入的相同的类是不能互相转换的。 <br>
 * 解决的办法是使用接口或者父类,只重新加载实现类或者子类即可。 <br>
 * 在自己实现的ClassLoader中,当需要加载接口或者父类的时候,要代理给父ClassLoader去加载 <br>
 * 
 * @author ...
 * @version 2012-11
 * @since jdk1.6.0
 */
public class DynamicEngine {

    private static Logger logger = Logger.getLogger(DynamicEngine.class);
    private static DynamicEngine instance = new DynamicEngine();
    private URLClassLoader parentClassLoader;
    private String classpath;

    public static DynamicEngine getInstance() {
        return instance;
    }

    private DynamicEngine() {
        this.parentClassLoader = (URLClassLoader) getClass().getClassLoader();
        buildClassPath();
    }

    private void buildClassPath() {
        StringBuilder sb = new StringBuilder();
        for (URL url : this.parentClassLoader.getURLs()) {
            String p = url.getFile();
            sb.append(p);
            sb.append(File.pathSeparator);
        }
        this.classpath = sb.toString();
    }

    /**
     * 编译Java代码(用来检查代码正确性)
     * 
     * @param className
     * @param javaCode
     * @return 编译通过则为null,不通过返回错误日志
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public String javaCodeCompile(String className, String javaCode) {
        long start = System.currentTimeMillis();
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        DiagnosticCollector diagListener = new DiagnosticCollector();
        ObjectFileManager fileManager = new ObjectFileManager(compiler.getStandardFileManager(diagListener, null, null));
        List<StringFileObject> compileUnits = new ArrayList<StringFileObject>(1);
        compileUnits.add(new StringFileObject(className, javaCode));
        List<String> options = new ArrayList<String>(4);
        options.add("-encoding");
        options.add("UTF-8");
        options.add("-classpath");
        options.add(this.classpath);
        JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagListener, options, null, compileUnits);
        boolean success = task.call().booleanValue();
        if (success) {
            long end = System.currentTimeMillis();
            logger.info("编译成功,用时:" + (end - start) + "ms");
        } else {
            StringBuilder error = new StringBuilder();
            for (Object diagnostic : diagListener.getDiagnostics()) {
                compilePrint(javaCode, error, (Diagnostic) diagnostic);
            }
            logger.error("编译失败:\n" + error);
            return error.toString();
        }
        return null;
    }

    /**
     * 编译Java代码(用来生成可用java对象)
     * 
     * @param className
     * @param javaCode
     * @return 编译通过返回相应对象,不通过则为null
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public Object javaCodeToObject(String className, String javaCode) throws Exception {
        Object result = null;
        long start = System.currentTimeMillis();
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        DiagnosticCollector diagListener = new DiagnosticCollector();
        ObjectFileManager fileManager = new ObjectFileManager(compiler.getStandardFileManager(diagListener, null, null));
        List<StringFileObject> compileUnits = new ArrayList<StringFileObject>(1);
        compileUnits.add(new StringFileObject(className, javaCode));
        List<String> options = new ArrayList<String>(4);
        options.add("-encoding");
        options.add("UTF-8");
        options.add("-classpath");
        options.add(this.classpath);
        JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagListener, options, null, compileUnits);
        boolean success = task.call().booleanValue();
        if (success) {
            ByteFileObject fileObject = fileManager.getCachedObject();
            DynamicClassLoader dynamicClassLoader = new DynamicClassLoader(this.parentClassLoader);
            Class clazz = dynamicClassLoader.loadClass(className, fileObject);
            result = clazz.newInstance();
            long end = System.currentTimeMillis();
            logger.info("编译成功,用时:" + (end - start) + "ms");
        } else {
            StringBuilder error = new StringBuilder();
            for (Object diagnostic : diagListener.getDiagnostics()) {
                compilePrint(javaCode, error, (Diagnostic) diagnostic);
            }
            logger.error("编译失败:\n" + error);
            return error.toString();
        }

        return result;
    }

    /**
     * 构造编译错误日志
     * 
     * @param javaCode
     * @param error
     * @param diagnostic
     */
    @SuppressWarnings("rawtypes")
    private void compilePrint(String javaCode, StringBuilder error, Diagnostic diagnostic) {
        error.append(diagnostic.getMessage(null));
        error.append('\n');
        error.append(getLine(javaCode, (int) diagnostic.getLineNumber()));
        error.append('\n');
        error.append(rjust("^", (int) diagnostic.getColumnNumber()));
        error.append('\n');
    }

    /**
     * 取源数据的指定行
     * 
     * @param source 源数据
     * @param line 行号
     * @return 确定的行
     */
    public String getLine(String source, int line) {
        char[] chars = source.toCharArray();
        int count = 1;
        int n = chars.length;
        int j = 0;
        for (int i = 0; i < n;) {
            // Find a line and append it
            while (i < n && chars[i] != '\n' && chars[i] != '\r'
                    && Character.getType(chars[i]) != Character.LINE_SEPARATOR) {
                i++;
            }
            // Skip the line break reading CRLF as one line break
            int eol = i;
            if (i < n) {
                if (chars[i] == '\r' && i + 1 < n && chars[i + 1] == '\n') {
                    i += 2;
                } else {
                    i++;
                }
            }
            if (count == line) {
                return source.substring(j, eol);
            } else {
                count++;
            }
            j = i;
        }
        if (j < n) {
            return source.substring(j, n);
        }
        return source;
    }

    /**
     * 左对齐(右方用空格填充)
     * 
     * @param src
     * @param width
     * @return
     */
    public String ljust(String src, int width) {
        return expand(src, width, ' ', true);
    }

    /**
     * 右对齐(左方用空格填充)
     * 
     * @param src
     * @param width
     * @return
     */
    public String rjust(String src, int width) {
        return expand(src, width, ' ', false);
    }

    private String expand(String src, int width, char fillchar, boolean postfix) {
        String result = src;
        if (result.length() < width) {
            char[] temp = new char[width - result.length()];
            for (int i = 0; i < temp.length; i++) {
                temp[i] = fillchar;
            }
            if (postfix) {
                result = result + new String(temp);
            } else {
                result = new String(temp) + result;
            }
        }
        return result;
    }
}


import java.net.URL;
import java.net.URLClassLoader;

/**
 * 自定义ClassLoader<br>
 * 定义一个ClassLoader,载入一个动态Class
 * 
 * @author ...
 * @version 2012-11
 * @since jdk1.6.0
 */
public class DynamicClassLoader extends URLClassLoader {
    public DynamicClassLoader(ClassLoader parent) {
        super(new URL[0], parent);
    }

    public Class<?> findClassByClassName(String className) throws ClassNotFoundException {
        return findClass(className);
    }

    public Class<?> loadClass(String className, ByteFileObject byteFileObject) {
        byte[] classData = byteFileObject.getBytes();
        return defineClass(className, classData, 0, classData.length);
    }
}


import java.io.IOException;
import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;

/**
 * 缓存编译对象
 * 
 * @author ...
 * @version 2012-11
 * @since jdk1.6.0
 */
@SuppressWarnings("rawtypes")
public class ObjectFileManager extends ForwardingJavaFileManager {
    private ByteFileObject currObject;

    @SuppressWarnings("unchecked")
    public ObjectFileManager(StandardJavaFileManager standardManager) {
        super(standardManager);
    }

    public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
        this.currObject = new ByteFileObject(className, kind);
        return this.currObject;
    }

    /**
     * 返回缓存的Object
     * 
     * @return
     */
    public ByteFileObject getCachedObject() {
        return this.currObject;
    }
}


import java.net.URI;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;

/**
 * 缓存字符串格式代码
 * 
 * @author ...
 * @version 2012-11
 * @since jdk1.6.0
 */
public class StringFileObject extends SimpleJavaFileObject {

    private String content;

    public StringFileObject(String className, String content) {
        super(URI.create("string:///" + className.replace('.', '/') +
                JavaFileObject.Kind.SOURCE.extension), JavaFileObject.Kind.SOURCE);
        this.content = content;
    }

    public CharSequence getCharContent(boolean ignoreEncodingErrors) {
        return this.content;
    }
}


import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;

/**
 * 缓存Byte流代码
 * 
 * @author ...
 * @version 2012-11
 * @since jdk1.6.0
 */
public class ByteFileObject extends SimpleJavaFileObject {

    protected final ByteArrayOutputStream bos = new ByteArrayOutputStream();

    public ByteFileObject(String name, JavaFileObject.Kind kind) {
        super(URI.create("string:///" + name.replace('.', '/') + kind.extension), kind);
    }

    public byte[] getBytes() {
        return this.bos.toByteArray();
    }

    public OutputStream openOutputStream() throws IOException {
        return this.bos;
    }
}



DynamicEngine de = DynamicEngine.getInstance();
de.javaCodeCompile(fullName, tpDataModel.getCode());

注意事项:内存编译代码暂时不支持指定包名,不定义包名即可
分享到:
评论

相关推荐

    java基础笔记

    编译时 javac 源文件名 java 运行的时候 java 含有main方法的 类名 注意:不要写 class 公有的 public 的类 其名字必须跟所在java源文件的文件名完全相同 main 方法在不在公有的类中没关系 非公有的类 可以跟源文件...

    java 基础的课堂笔记整理

    1.3当编译java程序报错, 2 1.4注意: 3 1.5DOS命令 3 1.6注释 3 1.7 实例演示: 3 1.7.1 HelloWorld程序 3 2.1八种基本类型: 4 2.2If分支语句 里面使用boolean来控制。 4 2.3作业: 4 2.3.1、使用swith case语句...

    java jdk8 学习笔记

    Java编译语言将Java代码编译成.class文件(只有一种形式),而C/C++语言将代码编译成01码,不同的操作系统的01码指令不同,这造成了不能跨平台,而Java将这个任务交给JVM,不同操作系统上的JVM将.class文件编译成不同...

    java基础学习笔记 java整合技术 java工具类.rar

    DOS操作 切换盘符: d: 进入文件夹:cd 文件夹名 命令提示: tab 一次性进入多个文件夹: cd\文件夹\文件夹 返回上级目录: cd … 进入当前目录: cd . ...java xx文件名(不要后缀) 运行编译后的java程序

    Java JDK 7学习笔记(国内第一本Java 7,前期版本累计销量5万册)

     《java jdk 7学习笔记》适合java的初中级读者,以及广大java应用开发人员。 作译者 林信良(网名:良葛格) 学历:台湾大学电机工程学系 经历:台湾升阳教育训练技术顾问、专业讲师,oracle授权训练中心讲师 ...

    JAVA--达内培训笔记

    JAVA--达内培训笔记】 1、Java之父Golsling 1995年5月23日 Java诞生 1998年12月 1.2版本 Java2 J2SE J2EE J2ME 2004年12月 1.5版本(5.0) Java JavaSE JavaEE JavaME 2、Java SE --- Java标准平台 Java EE -...

    java学习笔记java学习笔记

    答:1) 编程语言(先编译再解释); 2) 开发环境; 3) 运行环境; 4) 部署环境; 3. Java的特点 答:1) 简单(Java语法是C++语法的一个“纯净”版本); 2) 可移植性 3) 面向对象 4) 分布式(Java把打开套接字连接...

    java学习笔记,适合学习面试

    18天java基础学习笔记,写得非常详细,可以跟着学习。Java是一种先编译后解释的语言,所以它不如全编译性语言快。但是有些情况下性能是很要紧的,为了支持这些情况,Java设计者制作了“及时”编译程序,它能在运行时...

    \java超强笔记(超级经典)

    替代了Thread类,他可以创建定量的、动态的以及周期性的线程池。 ExecutorService接口: 线程池,用来存放线程来节省创建和销毁资源的消耗。 Callable和Future接口: Callable是类似于Runnable的接口,...

    ant 学习 笔记 一个简单的java 编译部署实例

    ant 学习笔记 ,自己的一些学的时候的一个例子

    corejava的学习笔记

    编译:javac ***.java 用法: javac &lt;options&gt; 用法:javac &lt;选项&gt; &lt;源文件&gt; 其中,可能的选项包括: -g 生成所有调试信息 -g:none 不生成任何调试信息 -g:{lines,vars,source} 只生成某些调试信息 -...

    北京圣思源JAVA课程笔记(全)

    ava SE 第一讲: Java SE:Java Standard Edition Java ME: Java Mobile Edition Java EE:Java Enterprise ...1. 编译 2. 执行 Class 文件是字节码文件,程序最终执行的就是这个字节码(bytecode)文件。

    观看韩顺平学习整理java的笔记到异常

    帮助大家复习java基础知识其中有 hashCode 2 toString 2 finalize 2 用已学知识做出简单的房屋出租系统 3 类方法使用注意事项和细节讨论 4 main()方法 4 代码块 4 代码块使用注意事项和细节 5 单例模式 6 final...

    Java面向对象程序设计笔记

    Java面向对象程序设计笔记: 目录----非原创,上课的时候老师给的资料哦 第一章 Java 前凑 3 1. Java前凑 3 Java产生的目的 3 Java的诞生的历史 3 2.Java语言的特点 3 简单性 3 面向对象 3 网络技能 3 3....

    动态代理及生成的代理类

    动态代理及其生成的代理类,可以反编译查看其类的结构。

    java笔记-帮助文档

    而JAVA不同, 他先是把自己的源代码编译成字节码(JVM语言),然后再将JVM 编译成相应的平台语言。 public class HelloWorld{ public static void main(String[] args){ System.ont.println("我的第一个JAVA程序!!")...

    java学习笔记 - 1

    1.java开发环境--java编译运行过程(常见面试题) 2.名词解释--JVM;JRE,JDK 3.配置环境变量 4.eclipse: 开发环境 开发步骤 注释

    java基础学习笔记之泛型

    所谓泛型,就是变量类型的参数化。泛型是JDK1.5中一个最重要的...通过引入泛型,我们将获得编译时类型的安全和运行时更小的抛出ClassCastException的可能。在JDK1.5中,你可以声明一个集合将接收/返回的对象的类型。

    核心Java笔记(高清晰,自己亲自制作)

    核心Java笔记(达内培训)高清晰(自己制作)带目录。 1) 简单(Java语法是C++语法的一个“纯净”版本); 2) 可移植性 (一次编译到处运行) 3) 面向对象 4) 分布式(Java把打开套接字连接等繁琐的网络任务变得非常...

Global site tag (gtag.js) - Google Analytics