`
oaklet
  • 浏览: 109047 次
  • 性别: 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基础 学习笔记 Markdownr版

    本学习笔记主要涵盖了Java的基础知识,包括面向对象、集合、IO流、多线程、反射与动态代理以及Java 8的新特性等方面,旨在帮助初学者或有经验的开发者巩固和提升Java编程技能。 1. 面向对象(OOP):Java的核心是...

    JAVA经典教材笔记.pdf

    开发环境的搭建通常包括安装JDK(Java Development Kit)和配置环境变量,以便能够编译和运行Java程序。 简单Java程序章节介绍了Java的基础程序设计,包括Java数据类型、运算符、表达式、语句、判断语句和循环语句...

    韩顺平编写的java学习笔记(全)

    ### 韩顺平编写的Java学习笔记概览 #### Java平台分类与运行机制 - **J2SE(Java 2 Platform, Standard Edition)**: 标准版Java开发平台,适用于桌面应用程序开发。 - **J2EE(Java 2 Platform, Enterprise ...

    (完整版)最全的java学习笔记(必看).pdf

    * 1.1 编程语言:Java是一种面向对象的编程语言,具有跨平台、动态加载、多线程等特点。Java语言的设计目标是提供一种通用的、基于对象的、高度面向对象的编程语言。 * 1.2 Java特点:Java语言的特点包括平台独立性...

    java全套笔记 来自狂神说java笔录.zip

    通过JDK,开发者可以编写、编译、运行和调试Java应用程序。 【IDEA(IntelliJ IDEA)】 IntelliJ IDEA是一款强大的Java集成开发环境,由JetBrains公司开发。它提供了代码自动完成、重构、调试、版本控制集成等多种...

    java笔记 java笔记

    - **高性能**:虽然Java是一种解释性语言,但其性能优化技术使其接近于编译型语言。 - **简单性**:Java语言设计简洁明了,易于学习和使用。 - **健壮性**:Java拥有严格的错误检测机制,有助于开发健壮的应用程序。...

    Java基础尚硅谷宋红康学习笔记

    1. **反射**:Java反射机制允许在运行时检查类的信息(如类名、方法、字段等),并能动态调用方法和修改字段值,增强了代码的灵活性。 2. **泛型**:泛型提供了一种在编译时检查类型安全的方法,允许在类、接口和...

    黑马java教程知识点笔记整理

    【Java编程基础】 ...以上是黑马Java教程知识点笔记的主要内容,涵盖了Java编程的基础到进阶知识,是学习和巩固Java技能的重要参考资料。通过深入理解和实践这些知识点,开发者可以逐步提升Java编程能力。

    java基础学习笔记

    ### Java基础学习笔记知识点 #### 一、Java学习路线概览 Java学习通常分为几个阶段,从基础到高级,逐步深化。以下是对给定文件中提到的学习路线的详细解析: ##### 第一阶段:Java基础 - **JAVASE**:Java标准版...

    女生的java初级课堂笔记

    【女生的Java初级课堂笔记概览】 这篇笔记主要是一位大学计算机系女生的Java学习记录,虽然重点不甚明确,但涵盖了Java的基础知识,适合初学者入门。笔记内容全面且易于理解,尤其对于女性学习者可能更有亲和力。 ...

    java基础整理笔记超详细

    【Java基础整理笔记超详细】 Java是一门广泛使用的高级编程语言,由Sun Microsystems(后被Oracle收购)在1995年推出。它的设计目标是跨平台、面向对象,并且具有健壮性和安全性。Java这个名字源于印度尼西亚的咖啡...

    java实训笔记

    Java 实训笔记主要涵盖Java语言的基础知识,包括JDK的安装、环境变量配置以及核心编程概念。以下是对这些知识点的详细说明: 1. **JDK安装与环境变量配置**: - JDK(Java Development Kit)是Java开发所需的核心...

    Java课堂笔记学习Java课堂笔记学习

    ### Java课堂笔记学习 #### 软件定义与软件开发 - **软件**:软件是运行在硬件之上的一组指令集,这些指令集能够完成特定的功能。软件开发过程包括设计和编码两个主要阶段。 #### 编程语言的重要性 - **编程语言...

    java优秀学习笔记

    ### Java优秀学习笔记知识点概述 #### 一、Java语言简介及特性 - **稳健性**:Java通过改进C++中的指针概念引入了引用的概念,从而增强了安全性。Java中的引用不可计算,避免了指针计算可能导致的问题。此外,Java...

    java经典教材笔记

    Java是一种广泛使用的面向...通过这些内容,可以看出Java经典教材笔记不仅覆盖了Java语言的基础知识点,还深入到面向对象编程、异常处理、多线程和泛型等高级概念。这本书籍对于巩固和深入理解Java语言具有很大的帮助。

    java基础笔记

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

    JAVA经典教材笔记

    ### JAVA经典教材笔记知识点梳理 #### 第一章:JAVA概述及开发环境搭建 - **Java学习概述** - Java作为一种广泛使用的编程语言,其学习路径包括掌握基本语法、理解面向对象编程思想、熟悉标准库使用等。 - **JAVA...

    Java随堂笔记

    这份“Java随堂笔记”正是为了帮助初学者以及希望巩固Java知识的开发者而编写的。笔记内容涵盖了Java的基础到进阶知识,特别是那些在企业开发中不可或缺的部分。 1. **Java基础** - **数据类型**:包括基本数据...

    毕向东Java笔记

    ### 毕向东Java笔记知识点总结 #### 第一章:编程基础 1. **Java的特性和优势**: - **简单性**:Java的设计哲学是“使编程变得简单”。 - **面向对象**:Java完全支持面向对象编程概念如封装、继承和多态。 - ...

    狂神说Java笔记资料

    《狂神说Java笔记资料》是一份全面涵盖Java开发基础知识的文档集,旨在帮助学习者系统地掌握Java编程。这份笔记包含多个章节,每个章节都深入讲解了一个特定的主题,覆盖了从初学者到进阶开发者必备的知识点。 1. *...

Global site tag (gtag.js) - Google Analytics