(0). 概述
前面对Compileflow有了一个入门,也说到它的核心原理,以及优缺点,这一小节,对优缺点进行难证(验证:流程图的改变,会触发ClassLoader进行热加载).
(1). AbstractProcessRuntime.compile
// 0. 定义生成java源代码类
private static final Compiler COMPILER = new CompilerImpl();
// 1. 编译源码
public void compile() {
compiledClassCache.computeIfAbsent(code, c -> compileJavaCode(getJavaCode(code)));
}
// 2. 获取源码信息
// <process id="ktv" name="ktv" isExecutable="true">
// code = ktv
private String getJavaCode(String code) {
return javaCodeCache.computeIfAbsent(code, c -> generateJavaCode());
}
// 3. 生成java代码
public String generateJavaCode() {
classTarget.addSuperInterface(ClassWrapper.of(ProcessInstance.class));
// ... ...
// 这部份内容忽略不计
generateFlowMethod("execute", this::generateExecuteMethodBody);
return classTarget.generateCode();
}
// 4. 编译java代码,并通过ClassLoader加载
private Class<?> compileJavaCode(String source) {
return COMPILER.compileJavaCode(classTarget.getFullName(), source);
}
(2). CompilerImpl
package com.alibaba.compileflow.engine.process.preruntime.compiler.impl;
import com.alibaba.compileflow.engine.common.CompileFlowException;
import com.alibaba.compileflow.engine.common.utils.FileUtils;
import com.alibaba.compileflow.engine.process.preruntime.compiler.Compiler;
import com.alibaba.compileflow.engine.process.preruntime.compiler.*;
import com.alibaba.compileflow.engine.process.preruntime.compiler.impl.support.EcJavaCompiler;
import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintWriter;
public class CompilerImpl implements Compiler {
// 0. 定义编译类
private static final JavaCompiler JAVA_COMPILER = new EcJavaCompiler();
@Override
public Class<?> compileJavaCode(
// compileflow.KtvFlow
String fullClassName,
// package compileflow; ... ...
String sourceCode) {
try {
// 1. 创建产生Java文件的目录(用户目录/.flowclasses)
// public static final String FLOW_COMPILE_CLASS_DIR = System.getProperty("user.dir") + File.separator
+ ".flowclasses" + File.separator;
String dirPath = CompileConstants.FLOW_COMPILE_CLASS_DIR;
File dirFile = new File(dirPath);
if (!dirFile.exists()) {
if (!dirFile.mkdir()) {
throw new RuntimeException(
"Output directory for process class can't be created, directory is " + dirPath);
}
}
// 2. 创建:KtvFlow.java文件
File javaSourceFile = writeJavaFile(dirFile, fullClassName, sourceCode);
// 3. Wrapper成JavaSource对象
JavaSource javaSource = JavaSource.of(javaSourceFile, sourceCode, fullClassName);
// ********************************************************
// 4. 委托给:EcJavaCompiler进行处理(实际底层又委托给了:org.eclipse.jdt.internal.compiler.Compiler)
// 先不关注这个.只需要知道这一步会产生:KtvFlow.class文件,因为,在这里我主要是验证:
// 当你对流程图中的变量进行改变时,是会触发ClassLoader重新加载的.
// ********************************************************
JAVA_COMPILER.compile(javaSource, new File(dirPath), new CompileOption());
// 5. 读取:KtvFlow.class文件
File classFile = new File(dirFile, fullClassName.replace('.', File.separatorChar) + ".class");
byte[] classBytes = FileUtils.readFileToByteArray(classFile);
// ********************************************************
// 6. 通过自定义的ClassLoader(FlowClassLoader)加载KtvFlow.class
// ********************************************************
return FlowClassLoader.getInstance().defineClass(fullClassName, classBytes);
} catch (CompileFlowException e) {
throw e;
} catch (Exception e) {
throw new CompileFlowException(e.getMessage(), e);
}
} // end compileJavaCode
}
(3). FlowClassLoader
package com.alibaba.compileflow.engine.process.preruntime.compiler.impl;
import com.alibaba.compileflow.engine.common.CompileFlowException;
import com.alibaba.compileflow.engine.process.preruntime.compiler.CompileConstants;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
public class FlowClassLoader extends URLClassLoader {
private static volatile FlowClassLoader instance = null;
public FlowClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}
public static FlowClassLoader getInstance() {
if (instance == null) {
synchronized (FlowClassLoader.class) {
if (instance == null) {
try {
// 定义要加载class的目录
URL url = new URL("file:///" + CompileConstants.FLOW_COMPILE_CLASS_DIR);
instance = new FlowClassLoader(new URL[] {url},
FlowClassLoader.class.getClassLoader());
} catch (MalformedURLException e) {
throw new CompileFlowException(e);
}
}
}
}
return instance;
}
// ********************************************************************
// 看到没有,流程图的改变,是会造成ClassLoader进行热加载.
// ********************************************************************
public void clearCache() {
instance = null;
}
// ********************************************************************
// 把字节码交给父类(URLClassLoader)进行加载.
// ********************************************************************
public Class<?> defineClass(String name, byte[] classBytes) {
return defineClass(name, classBytes, 0, classBytes.length);
}
}
(4). 总结
Compileflow相比责任链的实现,有它的优势,但是,缺点也很明确,其实,通过分析这个,你自己也能实现Class的热加载.