一、前言
这边基于alibaba的dubbo包中com.alibaba.dubbo.common.compiler自定义的编译器包,其中包括接口com.alibaba.dubbo.common.compiler.Compiler、抽象实现类com.alibaba.dubbo.common.compiler.AbstractCompiler及主要实现类com.alibaba.dubbo.common.compiler.JdkCompiler。
二、源码分析
1.Compiler接口定义
package com.alibaba.dubbo.common.compiler;@b@@b@import com.alibaba.dubbo.common.extension.SPI;@b@@b@@SPI("javassist")@b@public abstract interface Compiler@b@{@b@ public abstract Class<?> compile(String paramString, ClassLoader paramClassLoader);@b@}
2.AbstractCompiler抽象类
package com.alibaba.dubbo.common.compiler.support;@b@@b@import com.alibaba.dubbo.common.compiler.Compiler;@b@import com.alibaba.dubbo.common.utils.ClassHelper;@b@import java.util.regex.Matcher;@b@import java.util.regex.Pattern;@b@@b@public abstract class AbstractCompiler@b@ implements Compiler@b@{@b@ private static final Pattern PACKAGE_PATTERN = Pattern.compile("package\\s+([$_a-zA-Z][$_a-zA-Z0-9\\.]*);");@b@ private static final Pattern CLASS_PATTERN = Pattern.compile("class\\s+([$_a-zA-Z][$_a-zA-Z0-9]*)\\s+");@b@@b@ public Class<?> compile(String code, ClassLoader classLoader)@b@ {@b@ String pkg;@b@ String cls;@b@ code = code.trim();@b@ Matcher matcher = PACKAGE_PATTERN.matcher(code);@b@@b@ if (matcher.find())@b@ pkg = matcher.group(1);@b@ else@b@ pkg = "";@b@@b@ matcher = CLASS_PATTERN.matcher(code);@b@@b@ if (matcher.find())@b@ cls = matcher.group(1);@b@ else@b@ throw new IllegalArgumentException("No such class name in " + code);@b@@b@ String className = ((pkg != null) && (pkg.length() > 0)) ? pkg + "." + cls : cls;@b@ try {@b@ return Class.forName(className, true, ClassHelper.getCallerClassLoader(super.getClass()));@b@ } catch (ClassNotFoundException e) {@b@ if (!(code.endsWith("}")))@b@ throw new IllegalStateException("The java code not endsWith \"}\", code: \n" + code + "\n");@b@ try@b@ {@b@ return doCompile(className, code);@b@ } catch (RuntimeException t) {@b@ throw t;@b@ } catch (Throwable t) {@b@ throw new IllegalStateException("Failed to compile class, cause: " + t.getMessage() + ", class: " + className + ", code: \n" + code + "\n, stack: " + ClassUtils.toString(t));@b@ }@b@ }@b@ }@b@@b@ protected abstract Class<?> doCompile(String paramString1, String paramString2)@b@ throws Throwable;@b@}
3.主要实现类JdkCompiler
package com.alibaba.dubbo.common.compiler.support;@b@@b@import com.alibaba.dubbo.common.utils.ClassHelper;@b@import java.io.ByteArrayInputStream;@b@import java.io.ByteArrayOutputStream;@b@import java.io.File;@b@import java.io.IOException;@b@import java.io.InputStream;@b@import java.io.OutputStream;@b@import java.net.URI;@b@import java.net.URL;@b@import java.net.URLClassLoader;@b@import java.security.AccessController;@b@import java.security.PrivilegedAction;@b@import java.util.ArrayList;@b@import java.util.Arrays;@b@import java.util.Collection;@b@import java.util.Collections;@b@import java.util.Enumeration;@b@import java.util.HashMap;@b@import java.util.Iterator;@b@import java.util.List;@b@import java.util.Map;@b@import java.util.Set;@b@import javax.tools.DiagnosticCollector;@b@import javax.tools.FileObject;@b@import javax.tools.ForwardingJavaFileManager;@b@import javax.tools.JavaCompiler;@b@import javax.tools.JavaCompiler.CompilationTask;@b@import javax.tools.JavaFileManager;@b@import javax.tools.JavaFileManager.Location;@b@import javax.tools.JavaFileObject;@b@import javax.tools.JavaFileObject.Kind;@b@import javax.tools.SimpleJavaFileObject;@b@import javax.tools.StandardJavaFileManager;@b@import javax.tools.StandardLocation;@b@import javax.tools.ToolProvider;@b@@b@public class JdkCompiler extends AbstractCompiler@b@{@b@ private final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();@b@ private final DiagnosticCollector<JavaFileObject> diagnosticCollector = new DiagnosticCollector();@b@ private final ClassLoaderImpl classLoader;@b@ private final JavaFileManagerImpl javaFileManager;@b@ private volatile List<String> options;@b@@b@ public JdkCompiler()@b@ {@b@ this.options = new ArrayList();@b@ this.options.add("-target");@b@ this.options.add("1.6");@b@ StandardJavaFileManager manager = this.compiler.getStandardFileManager(this.diagnosticCollector, null, null);@b@ ClassLoader loader = Thread.currentThread().getContextClassLoader();@b@ if ((loader instanceof URLClassLoader) && (!(loader.getClass().getName().equals("sun.misc.Launcher$AppClassLoader"))))@b@ try@b@ {@b@ URLClassLoader urlClassLoader = (URLClassLoader)loader;@b@ List files = new ArrayList();@b@ URL[] arr$ = urlClassLoader.getURLs(); int len$ = arr$.length; for (int i$ = 0; i$ < len$; ++i$) { URL url = arr$[i$];@b@ files.add(new File(url.getFile()));@b@ }@b@ manager.setLocation(StandardLocation.CLASS_PATH, files);@b@ } catch (IOException e) {@b@ throw new IllegalStateException(e.getMessage(), e);@b@ }@b@@b@ this.classLoader = ((ClassLoaderImpl)AccessController.doPrivileged(new PrivilegedAction(this, loader) {@b@ public JdkCompiler.ClassLoaderImpl run() {@b@ return new JdkCompiler.ClassLoaderImpl(this.this$0, this.val$loader);@b@ }@b@@b@ }));@b@ this.javaFileManager = new JavaFileManagerImpl(manager, this.classLoader);@b@ }@b@@b@ public Class<?> doCompile(String name, String sourceCode) throws Throwable@b@ {@b@ int i = name.lastIndexOf(46);@b@ String packageName = (i < 0) ? "" : name.substring(0, i);@b@ String className = (i < 0) ? name : name.substring(i + 1);@b@ JavaFileObjectImpl javaFileObject = new JavaFileObjectImpl(className, sourceCode);@b@ this.javaFileManager.putFileForInput(StandardLocation.SOURCE_PATH, packageName, className + ".java", javaFileObject);@b@@b@ Boolean result = this.compiler.getTask(null, this.javaFileManager, this.diagnosticCollector, this.options, null, Arrays.asList(new JavaFileObject[] { javaFileObject })).call();@b@@b@ if ((result == null) || (!(result.booleanValue())))@b@ throw new IllegalStateException("Compilation failed. class: " + name + ", diagnostics: " + this.diagnosticCollector);@b@@b@ return this.classLoader.loadClass(name);@b@ }@b@@b@ private static final class JavaFileManagerImpl extends ForwardingJavaFileManager<JavaFileManager> {@b@ private final JdkCompiler.ClassLoaderImpl classLoader;@b@ private final Map<URI, JavaFileObject> fileObjects = new HashMap();@b@@b@ public JavaFileManagerImpl(JavaFileManager fileManager, JdkCompiler.ClassLoaderImpl classLoader) {@b@ super(fileManager);@b@ this.classLoader = classLoader;@b@ }@b@@b@ public FileObject getFileForInput(JavaFileManager.Location location, String packageName, String relativeName) throws IOException@b@ {@b@ FileObject o = (FileObject)this.fileObjects.get(uri(location, packageName, relativeName));@b@ if (o != null)@b@ return o;@b@ return super.getFileForInput(location, packageName, relativeName);@b@ }@b@@b@ public void putFileForInput(StandardLocation location, String packageName, String relativeName, JavaFileObject file) {@b@ this.fileObjects.put(uri(location, packageName, relativeName), file);@b@ }@b@@b@ private URI uri(JavaFileManager.Location location, String packageName, String relativeName) {@b@ return ClassUtils.toURI(location.getName() + '/' + packageName + '/' + relativeName);@b@ }@b@@b@ public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, String qualifiedName, JavaFileObject.Kind kind, FileObject outputFile)@b@ throws IOException@b@ {@b@ JavaFileObject file = new JdkCompiler.JavaFileObjectImpl(qualifiedName, kind);@b@ this.classLoader.add(qualifiedName, file);@b@ return file;@b@ }@b@@b@ public ClassLoader getClassLoader(JavaFileManager.Location location)@b@ {@b@ return this.classLoader;@b@ }@b@@b@ public String inferBinaryName(JavaFileManager.Location loc, JavaFileObject file)@b@ {@b@ if (file instanceof JdkCompiler.JavaFileObjectImpl)@b@ return file.getName();@b@ return super.inferBinaryName(loc, file);@b@ }@b@@b@ public Iterable<JavaFileObject> list(JavaFileManager.Location location, String packageName, Set<JavaFileObject.Kind> kinds, boolean recurse)@b@ throws IOException@b@ {@b@ JavaFileObject file;@b@ Iterable result = super.list(location, packageName, kinds, recurse);@b@@b@ ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();@b@ List urlList = new ArrayList();@b@ Enumeration e = contextClassLoader.getResources("com");@b@ while (e.hasMoreElements()) {@b@ urlList.add(e.nextElement());@b@ }@b@@b@ ArrayList files = new ArrayList();@b@@b@ if ((location == StandardLocation.CLASS_PATH) && (kinds.contains(JavaFileObject.Kind.CLASS))) {@b@ for (i$ = this.fileObjects.values().iterator(); i$.hasNext(); ) { file = (JavaFileObject)i$.next();@b@ if ((file.getKind() == JavaFileObject.Kind.CLASS) && (file.getName().startsWith(packageName)))@b@ files.add(file);@b@@b@ }@b@@b@ files.addAll(this.classLoader.files());@b@ } else if ((location == StandardLocation.SOURCE_PATH) && (kinds.contains(JavaFileObject.Kind.SOURCE))) {@b@ for (i$ = this.fileObjects.values().iterator(); i$.hasNext(); ) { file = (JavaFileObject)i$.next();@b@ if ((file.getKind() == JavaFileObject.Kind.SOURCE) && (file.getName().startsWith(packageName)))@b@ files.add(file);@b@ }@b@@b@ }@b@@b@ for (Iterator i$ = result.iterator(); i$.hasNext(); ) { file = (JavaFileObject)i$.next();@b@ files.add(file);@b@ }@b@@b@ return files;@b@ }@b@ }@b@@b@ private static final class JavaFileObjectImpl extends SimpleJavaFileObject@b@ {@b@ private ByteArrayOutputStream bytecode;@b@ private final CharSequence source;@b@@b@ public JavaFileObjectImpl(String baseName, CharSequence source)@b@ {@b@ super(ClassUtils.toURI(baseName + ".java"), JavaFileObject.Kind.SOURCE);@b@ this.source = source;@b@ }@b@@b@ JavaFileObjectImpl(String name, JavaFileObject.Kind kind) {@b@ super(ClassUtils.toURI(name), kind);@b@ this.source = null;@b@ }@b@@b@ public JavaFileObjectImpl(URI uri, JavaFileObject.Kind kind) {@b@ super(uri, kind);@b@ this.source = null;@b@ }@b@@b@ public CharSequence getCharContent(boolean ignoreEncodingErrors) throws UnsupportedOperationException@b@ {@b@ if (this.source == null)@b@ throw new UnsupportedOperationException("source == null");@b@@b@ return this.source;@b@ }@b@@b@ public InputStream openInputStream()@b@ {@b@ return new ByteArrayInputStream(getByteCode());@b@ }@b@@b@ public OutputStream openOutputStream()@b@ {@b@ return (this.bytecode = new ByteArrayOutputStream());@b@ }@b@@b@ public byte[] getByteCode() {@b@ return this.bytecode.toByteArray();@b@ }@b@ }@b@@b@ private final class ClassLoaderImpl extends ClassLoader@b@ {@b@ private final Map<String, JavaFileObject> classes = new HashMap();@b@@b@ ClassLoaderImpl(, ClassLoader paramClassLoader)@b@ {@b@ super(parentClassLoader);@b@ }@b@@b@ Collection<JavaFileObject> files() {@b@ return Collections.unmodifiableCollection(this.classes.values());@b@ }@b@@b@ protected Class<?> findClass() throws ClassNotFoundException@b@ {@b@ JavaFileObject file = (JavaFileObject)this.classes.get(qualifiedClassName);@b@ if (file != null) {@b@ byte[] bytes = ((JdkCompiler.JavaFileObjectImpl)file).getByteCode();@b@ return defineClass(qualifiedClassName, bytes, 0, bytes.length);@b@ }@b@ try {@b@ return ClassHelper.forNameWithCallerClassLoader(qualifiedClassName, getClass()); } catch (ClassNotFoundException nf) {@b@ }@b@ return super.findClass(qualifiedClassName);@b@ }@b@@b@ void add(, JavaFileObject javaFile)@b@ {@b@ this.classes.put(qualifiedClassName, javaFile);@b@ }@b@@b@ protected synchronized Class<?> loadClass(, boolean resolve) throws ClassNotFoundException@b@ {@b@ return super.loadClass(name, resolve);@b@ }@b@@b@ public InputStream getResourceAsStream()@b@ {@b@ if (name.endsWith(".class")) {@b@ String qualifiedClassName = name.substring(0, name.length() - ".class".length()).replace('/', '.');@b@ JdkCompiler.JavaFileObjectImpl file = (JdkCompiler.JavaFileObjectImpl)this.classes.get(qualifiedClassName);@b@ if (file != null)@b@ return new ByteArrayInputStream(file.getByteCode());@b@ }@b@@b@ return super.getResourceAsStream(name);@b@ }@b@ }@b@}