您的位置:首页 > 编程语言

Dubbo入门学习--动态代码编译器Compiler

2017-08-25 19:05 477 查看
在之前一篇博客中Dubbo入门学习--SPI实现@SPI和@Adaptive,我们已经了解到Dubbo通过生成动态代码来实现动态代理的,Dubbo提供了动态代码编译器接口Compiler。
Compiler接口:
@SPI("javassist")
public interface Compiler {

/**
* Compile java source code.
*
* @param code Java source code
* @param classLoader TODO
* @return Compiled class
*/
Class<?> compile(String code, ClassLoader classLoader);

}
Compiler接口提供了一个compile方法:
(1)code : 动态代码字符串
(2)classLoader :类加载器

Compiler接口实现类结构:

AdaptiveCompiler :简单来说是一个适配器类,通过参数来选择使用JdkCompiler还是JavassistCompiler

@Adaptive
public class AdaptiveCompiler implements Compiler {

//从配置文件中读取字符串
private static volatile String DEFAULT_COMPILER;

public static void setDefaultCompiler(String compiler) {
DEFAULT_COMPILER = compiler;
}

public Class<?> compile(String code, ClassLoader classLoader) {
Compiler compiler;
ExtensionLoader<Compiler> loader = ExtensionLoader.getExtensionLoader(Compiler.class);
String name = DEFAULT_COMPILER; // copy reference
//选择使用默认JavassistCompiler还是JdkCompiler
if (name != null && name.length() > 0) {
compiler = loader.getExtension(name);
} else {
compiler = loader.getDefaultExtension();
}
return compiler.compile(code, classLoader);
}

}
JavassistCompiler:使用Javassist提供的动态代码编译功能
public class JavassistCompiler extends AbstractCompiler {

private static final Pattern IMPORT_PATTERN = Pattern.compile("import\\s+([\\w\\.\\*]+);\n");

private static final Pattern EXTENDS_PATTERN = Pattern.compile("\\s+extends\\s+([\\w\\.]+)[^\\{]*\\{\n");

private static final Pattern IMPLEMENTS_PATTERN = Pattern.compile("\\s+implements\\s+([\\w\\.]+)\\s*\\{\n");

private static final Pattern METHODS_PATTERN = Pattern.compile("\n(private|public|protected)\\s+");

private static final Pattern FIELD_PATTERN = Pattern.compile("[^\n]+=[^\n]+;");

@Override
public Class<?> doCompile(String name, String source) throws Throwable {
int i = name.lastIndexOf('.');
String className = i < 0 ? name : name.substring(i + 1);
ClassPool pool = new ClassPool(true);
pool.appendClassPath(new LoaderClassPath(ClassHelper.getCallerClassLoader(getClass())));
Matcher matcher = IMPORT_PATTERN.matcher(source);
List<String> importPackages = new ArrayList<String>();
Map<String, String> fullNames = new HashMap<String, String>();
while (matcher.find()) {
String pkg = matcher.group(1);
if (pkg.endsWith(".*")) {
String pkgName = pkg.substring(0, pkg.length() - 2);
pool.importPackage(pkgName);
importPackages.add(pkgName);
} else {
int pi = pkg.lastIndexOf('.');
if (pi > 0) {
String pkgName = pkg.substring(0, pi);
pool.importPackage(pkgName);
importPackages.add(pkgName);
fullNames.put(pkg.substring(pi + 1), pkg);
}
}
}
String[] packages = importPackages.toArray(new String[0]);
matcher = EXTENDS_PATTERN.matcher(source);
CtClass cls;
if (matcher.find()) {
String extend = matcher.group(1).trim();
String extendClass;
if (extend.contains(".")) {
extendClass = extend;
} else if (fullNames.containsKey(extend)) {
extendClass = fullNames.get(extend);
} else {
extendClass = ClassUtils.forName(packages, extend).getName();
}
cls = pool.makeClass(name, pool.get(extendClass));
} else {
cls = pool.makeClass(name);
}
matcher = IMPLEMENTS_PATTERN.matcher(source);
if (matcher.find()) {
String[] ifaces = matcher.group(1).trim().split("\\,");
for (String iface : ifaces) {
iface = iface.trim();
String ifaceClass;
if (iface.contains(".")) {
ifaceClass = iface;
} else if (fullNames.containsKey(iface)) {
ifaceClass = fullNames.get(iface);
} else {
ifaceClass = ClassUtils.forName(packages, iface).getName();
}
cls.addInterface(pool.get(ifaceClass));
}
}
String body = source.substring(source.indexOf("{") + 1, source.length() - 1);
String[] methods = METHODS_PATTERN.split(body);
for (String method : methods) {
method = method.trim();
if (method.length() > 0) {
if (method.startsWith(className)) {
cls.addConstructor(CtNewConstructor.make("public " + method, cls));
} else if (FIELD_PATTERN.matcher(method).matches()) {
cls.addField(CtField.make("private " + method, cls));
} else {
cls.addMethod(CtNewMethod.make("public " + method, cls));
}
}
}
return cls.toClass(ClassHelper.getCallerClassLoader(getClass()), JavassistCompiler.class.getProtectionDomain());
}

}

JdkCompiler:使用JDK提供的动态代码编译功能
public class JdkCompiler extends AbstractCompiler {

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);
}

@Override
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(new JavaFileObject[]{javaFileObject})).call();
if (result == null || ! result.booleanValue()) {
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 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;
}

public JavaFileObjectImpl(URI uri, Kind kind){
super(uri, 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, 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<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() == 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() == Kind.SOURCE && file.getName().startsWith(packageName)) {
files.add(file);
}
}
}

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

return files;
}
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: