`
chenjingbo
  • 浏览: 459999 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

JdkCompiler 通过code动态编译

 
阅读更多

本篇主要是为了mark代码。在试验某个功能的时候,需要用到

写道
把对应的源代码编译成class对象,并load进系统

 的功能。然后想到在dubbo中有类似的代码,然后进行无耻的copy。对应的代码如下

 

package com.taobao.ju.hsfer.util;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.tools.DiagnosticCollector;
import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;

public class JdkCompiler {

    private final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

    private final DiagnosticCollector<JavaFileObject> diagnosticCollector = new DiagnosticCollector<JavaFileObject>();

    private final ClassLoaderImpl classLoader;

    private final JavaFileManagerImpl javaFileManager;

    private volatile List<String> options;

    public JdkCompiler(){
        options = new ArrayList<String>();
        options.add("-target");
        options.add("1.6");
        StandardJavaFileManager manager = compiler.getStandardFileManager(diagnosticCollector, null, null);
        final ClassLoader loader = Thread.currentThread().getContextClassLoader();
        if (loader instanceof URLClassLoader
                && (! loader.getClass().getName().equals("sun.misc.Launcher$AppClassLoader"))) {
            try {
                URLClassLoader urlClassLoader = (URLClassLoader) loader;
                List<File> files = new ArrayList<File>();
                for (URL url : urlClassLoader.getURLs()) {
                    files.add(new File(url.getFile()));
                }
                manager.setLocation(StandardLocation.CLASS_PATH, files);
            } catch (IOException e) {
                throw new IllegalStateException(e.getMessage(), e);
            }
        }
        classLoader = AccessController.doPrivileged(new PrivilegedAction<ClassLoaderImpl>() {
            public ClassLoaderImpl run() {
                return new ClassLoaderImpl(loader);
            }
        });
        javaFileManager = new JavaFileManagerImpl(manager, classLoader);
    }

    public Class<?> doCompile(String name, String sourceCode) throws Throwable {
        int i = name.lastIndexOf('.');
        String packageName = i < 0 ? "" : name.substring(0, i);
        String className = i < 0 ? name : name.substring(i + 1);
        JavaFileObjectImpl javaFileObject = new JavaFileObjectImpl(className, sourceCode);
        javaFileManager.putFileForInput(StandardLocation.SOURCE_PATH, packageName,
                className + ClassUtils.JAVA_EXTENSION, javaFileObject);
        Boolean result = compiler.getTask(null, javaFileManager, diagnosticCollector, options,
                null, Arrays.asList(javaFileObject)).call();
        if (result == null || !result) {
            throw new IllegalStateException("Compilation failed. class: " + name + ", diagnostics: " + diagnosticCollector);
        }
        return classLoader.loadClass(name);
    }

    private final class ClassLoaderImpl extends ClassLoader {

        private final Map<String, JavaFileObject> classes = new HashMap<String, JavaFileObject>();

        ClassLoaderImpl(final ClassLoader parentClassLoader) {
            super(parentClassLoader);
        }

        Collection<JavaFileObject> files() {
            return Collections.unmodifiableCollection(classes.values());
        }

        @Override
        protected Class<?> findClass(final String qualifiedClassName) throws ClassNotFoundException {
            JavaFileObject file = classes.get(qualifiedClassName);
            if (file != null) {
                byte[] bytes = ((JavaFileObjectImpl) file).getByteCode();
                return defineClass(qualifiedClassName, bytes, 0, bytes.length);
            }
            try {
                return  Thread.currentThread().getContextClassLoader().loadClass(qualifiedClassName);
//                return ClassHelper.forNameWithCallerClassLoader(qualifiedClassName, getClass());
            } catch (ClassNotFoundException nf) {
                return super.findClass(qualifiedClassName);
            }
        }

        void add(final String qualifiedClassName, final JavaFileObject javaFile) {
            classes.put(qualifiedClassName, javaFile);
        }

        @Override
        protected synchronized Class<?> loadClass(final String name, final boolean resolve) throws ClassNotFoundException {
            return super.loadClass(name, resolve);
        }

        @Override
        public InputStream getResourceAsStream(final String name) {
            if (name.endsWith(ClassUtils.CLASS_EXTENSION)) {
                String qualifiedClassName = name.substring(0, name.length() - ClassUtils.CLASS_EXTENSION.length()).replace('/', '.');
                JavaFileObjectImpl file = (JavaFileObjectImpl) classes.get(qualifiedClassName);
                if (file != null) {
                    return new ByteArrayInputStream(file.getByteCode());
                }
            }
            return super.getResourceAsStream(name);
        }
    }

    private static final class JavaFileObjectImpl extends SimpleJavaFileObject {

        private ByteArrayOutputStream bytecode;

        private final CharSequence    source;

        public JavaFileObjectImpl(final String baseName, final CharSequence source){
            super(ClassUtils.toURI(baseName + ClassUtils.JAVA_EXTENSION), Kind.SOURCE);
            this.source = source;
        }

        JavaFileObjectImpl(final String name, final Kind kind){
            super(ClassUtils.toURI(name), kind);
            source = null;
        }

        @Override
        public CharSequence getCharContent(final boolean ignoreEncodingErrors) throws UnsupportedOperationException {
            if (source == null) {
                throw new UnsupportedOperationException("source == null");
            }
            return source;
        }

        @Override
        public InputStream openInputStream() {
            return new ByteArrayInputStream(getByteCode());
        }

        @Override
        public OutputStream openOutputStream() {
            return bytecode = new ByteArrayOutputStream();
        }

        public byte[] getByteCode() {
            return bytecode.toByteArray();
        }
    }

    private static final class JavaFileManagerImpl extends ForwardingJavaFileManager<JavaFileManager> {

        private final ClassLoaderImpl classLoader;

        private final Map<URI, JavaFileObject> fileObjects = new HashMap<URI, JavaFileObject>();

        public JavaFileManagerImpl(JavaFileManager fileManager, ClassLoaderImpl classLoader) {
            super(fileManager);
            this.classLoader = classLoader;
        }

        @Override
        public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException {
            FileObject o = fileObjects.get(uri(location, packageName, relativeName));
            if (o != null)
                return o;
            return super.getFileForInput(location, packageName, relativeName);
        }

        public void putFileForInput(StandardLocation location, String packageName, String relativeName, JavaFileObject file) {
            fileObjects.put(uri(location, packageName, relativeName), file);
        }

        private URI uri(Location location, String packageName, String relativeName) {
            return ClassUtils.toURI(location.getName() + '/' + packageName + '/' + relativeName);
        }

        @Override
        public JavaFileObject getJavaFileForOutput(Location location, String qualifiedName, JavaFileObject.Kind kind, FileObject outputFile)
                throws IOException {
            JavaFileObject file = new JavaFileObjectImpl(qualifiedName, kind);
            classLoader.add(qualifiedName, file);
            return file;
        }

        @Override
        public ClassLoader getClassLoader(JavaFileManager.Location location) {
            return classLoader;
        }

        @Override
        public String inferBinaryName(Location loc, JavaFileObject file) {
            if (file instanceof JavaFileObjectImpl)
                return file.getName();
            return super.inferBinaryName(loc, file);
        }

        @Override
        public Iterable<JavaFileObject> list(Location location, String packageName, Set<JavaFileObject.Kind> kinds, boolean recurse)
                throws IOException {
            Iterable<JavaFileObject> result = super.list(location, packageName, kinds, recurse);

//            ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
//            List<URL> urlList = new ArrayList<URL>();
//            Enumeration<URL> e = contextClassLoader.getResources("com");
//            while (e.hasMoreElements()) {
//                urlList.add(e.nextElement());
//            }

            ArrayList<JavaFileObject> files = new ArrayList<JavaFileObject>();

            if (location == StandardLocation.CLASS_PATH && kinds.contains(JavaFileObject.Kind.CLASS)) {
                for (JavaFileObject file : fileObjects.values()) {
                    if (file.getKind() == JavaFileObject.Kind.CLASS && file.getName().startsWith(packageName)) {
                        files.add(file);
                    }
                }

                files.addAll(classLoader.files());
            } else if (location == StandardLocation.SOURCE_PATH && kinds.contains(JavaFileObject.Kind.SOURCE)) {
                for (JavaFileObject file : fileObjects.values()) {
                    if (file.getKind() == JavaFileObject.Kind.SOURCE && file.getName().startsWith(packageName)) {
                        files.add(file);
                    }
                }
            }

            for (JavaFileObject file : result) {
                files.add(file);
            }

            return files;
        }
    }

    public static void main(String[] args) throws Throwable {
        StringBuilder sb = new StringBuilder();
        sb.append("package zhenghui;").append("\n");
        sb.append("public class Test1{").append("\n");
        sb.append("    public void printHelloWorld(){").append("\n");
        sb.append("System.out.println(\"Hello Wolrd\");").append("\n");
        sb.append("    }").append("\n");
        sb.append("}").append("\n");
        JdkCompiler jdkCompiler = new JdkCompiler();
        Class clazz = jdkCompiler.doCompile("zhenghui.Test1",sb.toString());
        Object obj = clazz.newInstance();
        Method method = clazz.getMethod("printHelloWorld");
        method.invoke(obj);
    }


}

 

分享到:
评论

相关推荐

    通过JavaCompiler进行编译java文件(转载)

    总的来说,通过JavaCompiler API,开发者可以轻松地在Java应用程序中实现动态编译的功能,这对于需要在运行时生成或修改代码的项目来说是一大利器。不过,需要注意的是,这种编译方式可能会增加程序的复杂性,并且在...

    JAVAC动态编译

    动态编译在Java中主要通过`javax.tools.JavaCompiler`接口和`java.lang.reflect.Proxy`类来实现。`JavaCompiler`接口是Java 6引入的,允许程序在运行时调用JDK的编译器来编译源代码。这个功能对于构建工具、自动化...

    eclise中安装JAVA反编译工具jad

    在Java开发过程中,有时我们需要查看已编译的.class文件源代码,这时就需要用到反编译工具。Eclipse是一款广泛使用的Java集成开发环境,而JAD则是常用的Java反编译器,它可以将字节码转换回接近源代码的形式。本文将...

    在程序中实现对java源文件编译的3种方法文.pdf

    本方法适用于在Java程序内部动态编译源代码的情形,例如需要通过放置一些源代码来实现程序功能的动态扩展。此方法通过调用`Runtime`类或`ProcessBuilder`类来执行`javac`命令。 **准备工作**: 1. **源文件路径**...

    CodeWorksforAndroid-1R4-windows

    CodeWorks for Android-1R4-windows.exe是该工具的安装程序,运行后会自动配置所需的环境,包括NDK(Android Native Development Kit)、JDK(Java Development Kit)和必要的编译工具链。确保在安装前已安装好这些...

    scratchcompiler:该项目是将Java源代码编译为可运行的草稿项目的工具。 (前Alpha!)-java project source code

    解压缩后的一些Java编码实践(但是您可以通过查看示例并稍作更改来学习它),您将看到: 文件夹javacode &lt;-您将代码放在这里文件夹项目&lt;-请勿触摸存档generate.sb2 &lt;-您的项目! 存档scratchcompiler.jar ...

    超详细的jdk,Myeclipse的安装和使用

    JDK是Oracle公司提供的Java编程语言的软件开发包,它包含了编译、运行Java程序所需的所有组件,包括Java虚拟机(JVM)、Java类库以及相关的开发工具。MyEclipse则是基于Eclipse的集成开发环境(IDE),专为Java、Web...

    jdk1.7_80 window.64

    **Java Development Kit (JDK) 1.7 ...开发者可以利用这个JDK来编写、编译、调试和运行Java应用程序,同时享受到Java 7的新特性带来的效率提升和编程便利。在开发过程中,记得定期更新到最新的安全补丁以保持系统安全。

    Windows下Android开发环境搭建指南

    确保Eclipse使用正确的JDK版本进行编译,通过【Window】&gt;【Preferences…】打开属性设置窗口,选择【Java】&gt;【Compiler】面板,设置Java编译器为JDK 6.0版本。 #### 六、创建Android项目 完成上述步骤后,即可在...

    虚拟机Virtual box的Linux环境配置教程

    在Ubuntu系统中,gcc(GNU Compiler Collection)是编译C和C++代码的必备工具。通过在终端执行sudo apt-get install build-essential命令,可以安装gcc及其依赖的开发工具包。 配置gedit是为了让文本编辑器gedit...

    深入理解jvm源码

    - **编译策略**:如C1(Client Compiler)和C2(Server Compiler),以及Graal编译器等。 5. **类文件结构**: - **魔数**:标识文件为合法的Class文件。 - **常量池**:存储各种常量和符号引用。 - **字段表、...

    sourceCode:Java原始码分析

    在运行时,JVM的解释器会逐行解释执行字节码,或者使用即时编译器(JIT,Just-In-Time Compiler)将频繁执行的热点代码编译为本地机器码,以提高程序性能。这就是Java的动态编译和优化机制。 为了分析Java源码,...

    compiler:JAVA语言编译器

    Java的动态性体现在JVM上,因为JVM不仅负责运行字节码,还可以通过即时编译(Just-In-Time,JIT)将频繁执行的热点代码编译成本地机器码,以获得更好的性能。JIT编译器是Java性能优化的关键部分,它能够根据运行时的...

    eclipse最佳设置

    在`Window &gt; Preferences &gt; Java &gt; Compiler`中,可以调整编译器的警告级别,对未使用的变量、过时的方法等进行提醒。同时,启用"Build automatically"选项,可以让Eclipse在每次保存时自动编译,及时发现错误。 ...

    maven配置工具

    1. **处理@Override报错**:当出现@Override报错时,需要在`pom.xml`中添加Maven编译插件配置,指定JDK版本,例如: ```xml &lt;groupId&gt;org.apache.maven.plugins &lt;artifactId&gt;maven-compiler-plugin &lt;version&gt;...

    MyEclispe工作空间环境配置

    - **目的**:通过此界面可以进行各种个性化设置,包括编码格式、JDK版本、代码检查规则等,从而优化开发体验。 ##### 2.2 设置UTF-8字符集 - **步骤**:在“Preferences”对话框中,找到“General”&gt;“Workspace”...

    eclipse详细配置

    在“Java Compiler”中可以设定项目的编译级别,确保代码与目标JDK版本兼容。 7. **智能提示选项**:在“Content Assist”的“Advanced”选项中,可以设置哪些类型的提示应该显示。例如,添加“Java Proposals”和...

    Javac内核源码

    OpenJDK 是一个由社区驱动的开源项目,它实现了完整的 Java 开发工具包(JDK),包括 Javac。通过研究 Javac 的源码,我们可以了解到以下几个关键知识点: 1. **词法分析**:源代码首先被解析成一个个的标记(Token...

    eclipse 常用设置

    在Eclipse中,可以通过`Window` -&gt; `Preferences` -&gt; `Java` -&gt; `Code Style` -&gt; `Code Templates`来访问和编辑这些模板。 1. **代码格式化**:Eclipse允许用户自定义代码格式化规则,包括缩进、空格、行宽等。这...

    IDEA开发环境设置.doc

    - 热部署通过Registry设置"compiler.automake.allow.when.app.running"。 - 引入`spring-boot-devtools`依赖,实现热部署。 - 组合键"Shift+Ctrl+Alt+/ "打开Registry,开启相关选项。 以上是IDEA开发环境的基本...

Global site tag (gtag.js) - Google Analytics