java解决动态编译加载不到import的jar包

package com.*.utils;

import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

public class DynamicLoader {

    /**
     * 通过类名和其代码(Java代码字符串),编译得到字节码,返回类名及其对应类的字节码,封装于Map中,值得注意的是,
     * 平常类中就编译出来的字节码只有一个类,但是考虑到内部类的情况, 会出现很多个类名及其字节码,所以用Map封装方便。
     *
     * @param javaName 类名
     * @param javaSrc  Java源码
     * @return map
     */
    public static Map<String, Object> compile(String javaName, String javaSrc) throws Exception {
        Map<String, Object> map = new HashMap<>();
        // 调用java编译器接口
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager stdManager = compiler.getStandardFileManager(null, null, null);

        URL[] s = ((URLClassLoader) DynamicLoader.class.getClassLoader()).getURLs();
        URLClassLoader loader = new URLClassLoader(s, Thread.currentThread().getContextClassLoader());
        MemoryJavaFileManager manager = new MemoryJavaFileManager(loader, stdManager);
        JavaFileObject javaFileObject = MemoryJavaFileManager.makeStringSource(javaName, javaSrc);

        JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null,
                Arrays.asList(javaFileObject));

        boolean status = task.call();
        map.put("status", status);
        if (status) {
            map.put("byte", manager.getClassBytes());
        }
        return map;
    }

    /**
     * 先根据类名在内存中查找是否已存在该类,若不存在则调用 URLClassLoader的 defineClass方法加载该类
     * URLClassLoader的具体作用就是将class文件加载到jvm虚拟机中去
     *
     * @author Administrator
     *
     */
    public static class MemoryClassLoader extends ClassLoader {
        // private static final String CLASS_LOADER_NAME = "MemoryClassLoader";
        Map<String, byte[]> classBytes = new HashMap<String, byte[]>();

        public MemoryClassLoader(ClassLoader parent, Map<String, byte[]> classBytes) {
            super(parent);
            this.classBytes.putAll(classBytes);
        }

        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            byte[] buf = classBytes.get(name);
            if (buf == null) {
                return super.findClass(name);
            }
            classBytes.remove(name);
            return defineClass(name, buf, 0, buf.length);
        }
    }
}

package com.*.utils;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringReader;
import java.io.Writer;
import java.net.JarURLConnection;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.CharBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarEntry;

import javax.lang.model.element.Modifier;
import javax.lang.model.element.NestingKind;
import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardLocation;

/**
 * 将编译好的.class文件保存到内存当中,这里的内存也就是map映射当中
 */
@SuppressWarnings("rawtypes")
public final class MemoryJavaFileManager extends ForwardingJavaFileManager {

    private final static String EXT = ".java";// Java源文件的扩展名
    private Map<String, byte[]> classBytes;// 用于存放.class文件的内存
    private URLClassLoader classLoader;

    @SuppressWarnings("unchecked")
    public MemoryJavaFileManager(URLClassLoader classLoader, JavaFileManager fileManager) {
        super(fileManager);
        classBytes = new HashMap<String, byte[]>();
        this.classLoader = classLoader;
    }

    public Map<String, byte[]> getClassBytes() {
        return classBytes;
    }

    @Override
    public void close() throws IOException {
        classBytes = new HashMap<String, byte[]>();
    }

    @Override
    public void flush() throws IOException {
    }

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

    @Override
    public String inferBinaryName(Location location, JavaFileObject file) {
        String ret = "";
        if (file instanceof CustomJavaFileObject) {
            ret = ((CustomJavaFileObject) file).binaryName;
        } else {
            ret = super.inferBinaryName(location, file);
        }
        return ret;
    }

    @SuppressWarnings("unchecked")
    @Override
    public Iterable<JavaFileObject> list(Location location, String packageName, Set kinds, boolean recurse)
            throws IOException {
        Iterable<JavaFileObject> ret = null;
        if (location == StandardLocation.PLATFORM_CLASS_PATH) {
            ret = super.list(location, packageName, kinds, recurse);
        } else if (location == StandardLocation.CLASS_PATH && kinds.contains(JavaFileObject.Kind.CLASS)) {
            ret = find(packageName);
            if (ret == null || (!ret.iterator().hasNext())) {
                ret = super.list(location, packageName, kinds, recurse);
            }
        } else {
            ret = Collections.emptyList();
        }
        return ret;
    }

    public static class CustomJavaFileObject implements JavaFileObject {
        private String binaryName;
        private URI uri;
        private String name;

        public String binaryName() {
            return binaryName;
        }

        public CustomJavaFileObject(String binaryName, URI uri) {
            this.uri = uri;
            this.binaryName = binaryName;
            name = uri.getPath() == null ? uri.getSchemeSpecificPart() : uri.getPath();
        }

        @Override
        public Kind getKind() {
            return Kind.CLASS;
        }

        @Override
        public boolean isNameCompatible(String simpleName, Kind kind) {
            String baseName = simpleName + kind.extension;
            return kind.equals(getKind()) && (baseName.equals(getName()) || getName().endsWith("/" + baseName));
        }

        @Override
        public NestingKind getNestingKind() {
            throw new UnsupportedOperationException();
        }

        @Override
        public Modifier getAccessLevel() {
            throw new UnsupportedOperationException();
        }

        @Override
        public URI toUri() {
            return uri;
        }

        @Override
        public String getName() {
            return name;
        }

        @Override
        public InputStream openInputStream() throws IOException {
            return uri.toURL().openStream();
        }

        @Override
        public OutputStream openOutputStream() throws IOException {
            throw new UnsupportedOperationException();
        }

        @Override
        public Reader openReader(boolean ignoreEncodingErrors) throws IOException {
            throw new UnsupportedOperationException();
        }

        @Override
        public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
            throw new UnsupportedOperationException();
        }

        @Override
        public Writer openWriter() throws IOException {
            throw new UnsupportedOperationException();
        }

        @Override
        public long getLastModified() {
            return 0;
        }

        @Override
        public boolean delete() {
            throw new UnsupportedOperationException();
        }
    }

    private List<JavaFileObject> find(String packageName) {
        List<JavaFileObject> result = new ArrayList<>();
        String javaPackageName = packageName.replaceAll("\\.", "/");
        try {
            Enumeration<URL> urls = classLoader.findResources(javaPackageName);
            while (urls.hasMoreElements()) {
                URL ll = urls.nextElement();
                String ext_form = ll.toExternalForm();
                if (ext_form.lastIndexOf("!") > 0) {
                    String jar = ext_form.substring(0, ext_form.lastIndexOf("!"));
                    String pkg = ext_form.substring(ext_form.lastIndexOf("!") + 1);

                    JarURLConnection conn = (JarURLConnection) ll.openConnection();
                    conn.connect();
                    Enumeration<JarEntry> jar_items = conn.getJarFile().entries();
                    while (jar_items.hasMoreElements()) {
                        JarEntry item = jar_items.nextElement();
                        if (item.isDirectory() || (!item.getName().endsWith(".class"))) {
                            continue;
                        }
                        if (item.getName().lastIndexOf("/") != (pkg.length() - 1)) {
                            continue;
                        }
                        String name = item.getName();
                        URI uri = URI.create(jar + "!/" + name);
                        String binaryName = name.replaceAll("/", ".");
                        binaryName = binaryName.substring(0, binaryName.indexOf(JavaFileObject.Kind.CLASS.extension));
                        result.add(new CustomJavaFileObject(binaryName, uri));
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 一个文件对象,用来表示从string中获取到的source,一下类容是按照jkd给出的例子写的
     */
    private static class StringInputBuffer extends SimpleJavaFileObject {
        // The source code of this "file".
        final String code;

        /**
         * Constructs a new JavaSourceFromString.
         *
         * @param name 此文件对象表示的编译单元的name
         * @param code 此文件对象表示的编译单元source的code
         */
        StringInputBuffer(String name, String code) {
            super(toURI(name), Kind.SOURCE);
            this.code = code;
        }

        @Override
        public CharBuffer getCharContent(boolean ignoreEncodingErrors) {
            return CharBuffer.wrap(code);
        }

        @SuppressWarnings("unused")
        public Reader openReader() {
            return new StringReader(code);
        }
    }

    /**
     * 将Java字节码存储到classBytes映射中的文件对象
     */
    private class ClassOutputBuffer extends SimpleJavaFileObject {
        private String name;

        /**
         * @param name className
         */
        ClassOutputBuffer(String name) {
            super(toURI(name), Kind.CLASS);
            this.name = name;
        }

        @Override
        public OutputStream openOutputStream() {
            return new FilterOutputStream(new ByteArrayOutputStream()) {
                @Override
                public void close() throws IOException {
                    out.close();
                    ByteArrayOutputStream bos = (ByteArrayOutputStream) out;

                    // 这里需要修改
                    classBytes.put(name, bos.toByteArray());
                }
            };
        }
    }

    @Override
    public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, String className,
            JavaFileObject.Kind kind, FileObject sibling) throws IOException {
        if (kind == JavaFileObject.Kind.CLASS) {
            return new ClassOutputBuffer(className);
        } else {
            return super.getJavaFileForOutput(location, className, kind, sibling);
        }
    }

    static JavaFileObject makeStringSource(String name, String code) {
        return new StringInputBuffer(name, code);
    }

    static URI toURI(String name) {
        File file = new File(name);
        if (file.exists()) {// 如果文件存在,返回他的URI
            return file.toURI();
        } else {
            try {
                final StringBuilder newUri = new StringBuilder();
                newUri.append("mfm:///");
                newUri.append(name.replace('.', '/'));
                if (name.endsWith(EXT)) {
                    newUri.replace(newUri.length() - EXT.length(), newUri.length(), EXT);
                }
                return URI.create(newUri.toString());
            } catch (Exception exp) {
                return URI.create("mfm:///com/sun/script/java/java_source");
            }
        }
    }
}

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值