10、java编译原理
2020-08-21 本文已影响0人
霸体
javac编译java源代码主要分为4个过程:
1、词法分析:通过词法分析器将java源文件处理为token流;
2、语法分析:通过语法分析器,初步生成抽象语法树,抽象语法树由sun包里的一组内部类实现;
3、语法优化:这个阶段包含复杂语法的优化(如foreach)、默认构造函数创建、源代码注解处理等等;
4、字节码生成:通过sun内部类Gen遍历抽象语法树,生成jvm可识别的字节码文件;
编译阶段包含上面的4个过程,我们可以干预第三个阶段,通过注解来干预源代码的生成;
Android 很多框架都是 “编译时”框架,java服务器大部分都是 “运行时”框架, 因为客户端的性能要求更高往往要求提前编译好,而服务器一般使用 “反射”能力,做服务端的运行时动态处理;
“编译时”框架,通常都是参考RSF269标准(从JDK1.6开始支持),服务器最常用的Lombok插件就是基于此开发的。
通常情况下,编译时处理器都是做一些简单校验或者生成新的java源代码文件,这时候不需要使用JDK编译器的内部API。
如果要想Lombok那样修改原始的java代码,需要熟悉Java编译器的内部API和AST(抽象语法树)。
public class Log {
public static void print(String msg){
System.out.println(msg);
}
}
//定义源代码处理器的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Probe {
}
//demo版本 源代码处理器
public class ProbeProcessor extends AbstractProcessor {
/**
* AST 抽象语法树
*/
private Trees trees;
/**
* 语法树节点工具类
*/
private TreeMaker make;
/**
* 标识符工具类
*/
private Name.Table names;
/**
* java compiler 内部上下文
*/
private Context context;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, ">>> probe processor 初始化了! ");
trees = Trees.instance(processingEnv);
context = ((JavacProcessingEnvironment)
processingEnv).getContext();
make = TreeMaker.instance(context);
names = Names.instance(context).table;
}
/**
* 针对特定的java类
*
* @return
*/
@Override
public Set<String> getSupportedAnnotationTypes() {
HashSet<String> supportTypes = new LinkedHashSet<>();
supportTypes.add(Probe.class.getCanonicalName());
return supportTypes;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.RELEASE_8;
}
/**
* 源代码修改入口
* 注意此方法会被调用多次
*
* @param annotations
* @param roundEnv
* @return
*/
@Override
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment roundEnv) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, " >>> probe processor execute. ");
if (!roundEnv.processingOver()) {
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Probe.class);
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, " >>> probe processor execute and size= " + elements.size());
for (Element each : elements) {
if (each.getKind() != ElementKind.CLASS) {
continue;
}
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,
" >>> handing probed class " + ((TypeElement) each).getQualifiedName().toString());
//添加工具包import
// addImportInfo(each);
//修改java compiler的抽象语法树中的方法定义
JCTree tree = (JCTree) trees.getTree(each);
TreeTranslator visitor = new ProbeTranslator();
tree.accept(visitor);
}
} else {
//round结束后,还会调用所有的注解处理器一次
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, ">>> probe modify source code finished.");
}
//true 代表独占 false代表共享,会继续被其他注解处理器继续处理
return true;
}
private void addImportInfo(Element element) {
TreePath treePath = trees.getPath(element);
Tree leaf = treePath.getLeaf();
if (treePath.getCompilationUnit() instanceof JCTree.JCCompilationUnit && leaf instanceof JCTree) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, ">>> addImportInfo if success.");
JCTree.JCCompilationUnit jccu = (JCTree.JCCompilationUnit) treePath.getCompilationUnit();
for (JCTree jcTree : jccu.getImports()) {
if (jcTree != null && jcTree instanceof JCTree.JCImport) {
JCTree.JCImport jcImport = (JCTree.JCImport) jcTree;
if (jcImport.qualid != null && jcImport.qualid instanceof JCTree.JCFieldAccess) {
JCTree.JCFieldAccess jcFieldAccess = (JCTree.JCFieldAccess) jcImport.qualid;
try {
if ("zxl.org.processor".equals(jcFieldAccess.selected.toString()) && "Log".equals(jcFieldAccess.name.toString())) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, ">>> addImportInfo contains zxl.Log , so return");
return;
}
} catch (NullPointerException e) {
e.printStackTrace();
}
}
}
}
java.util.List<JCTree> trees = new ArrayList<>();
trees.addAll(jccu.defs);
JCTree.JCIdent ident = make.Ident(names.fromString("zxl.org.processor"));
JCTree.JCImport jcImport = make.Import(make.Select(ident, names.fromString("Log")), false);
if (!trees.contains(jcImport)) {
trees.add(0, jcImport);
}
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, ">>> addImportInfo finished.");
jccu.defs = List.from(trees);
}
}
private class ProbeTranslator extends TreeTranslator {
@Override
public void visitMethodDef(JCTree.JCMethodDecl jcMethodDecl) {
super.visitMethodDef(jcMethodDecl);
if (jcMethodDecl.getName().toString().equalsIgnoreCase("<init>")) {
return;
}
JCTree.JCBlock body = jcMethodDecl.getBody();
JCTree.JCExpressionStatement printVar = make.Exec(make.Apply(
List.of(memberAccess("java.lang.String")),//参数类型
memberAccess("zxl.org.processor.Log.print"),
List.of(make.Literal("Auto Generated."))
));
List<JCTree.JCStatement> modifiedStatements = body.getStatements();
modifiedStatements = modifiedStatements.prepend(printVar);
modifiedStatements = modifiedStatements.append(printVar);
body.stats = modifiedStatements;
JCTree.JCMethodDecl methodDecl = make.MethodDef(jcMethodDecl.getModifiers(), jcMethodDecl.getName(),
jcMethodDecl.restype, jcMethodDecl.getTypeParameters(), jcMethodDecl.getParameters(), jcMethodDecl.getThrows(),
body, jcMethodDecl.defaultValue);
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, ">>> visitMethodDef finished. methodDecl=" + methodDecl +
" stats=" + body.stats);
result = methodDecl;
}
private JCTree.JCExpression memberAccess(String components) {
String[] componentArray = components.split("\\.");
JCTree.JCExpression expr = make.Ident(names.fromString(componentArray[0]));
for (int i = 1; i < componentArray.length; i++) {
expr = make.Select(expr, names.fromString(componentArray[i]));
}
return expr;
}
@Override
public void visitImport(JCTree.JCImport jcImport) {
super.visitImport(jcImport);
}
}
}
//稍微复杂一点的实现
import com.google.common.collect.Lists;
import com.sun.source.util.Trees;
import com.sun.tools.javac.code.TypeTag;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.TreeTranslator;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.Name;
import com.sun.tools.javac.util.Names;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.TreeMap;
/**
* java编译器的注解处理器
* 需要在mvn compiler插件中激活
*
* @author zhouxiliang
* @date 2020/7/19 17:11
*/
public class ProbeProcessor extends AbstractProcessor {
/**
* AST 抽象语法树
*/
private Trees trees;
/**
* 语法树节点工具类
*/
private TreeMaker make;
/**
* 标识符工具类
*/
private Name.Table names;
/**
* java compiler 内部上下文
*/
private Context context;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "probe processor initialized.");
trees = Trees.instance(processingEnv);
context = ((JavacProcessingEnvironment)
processingEnv).getContext();
make = TreeMaker.instance(context);
names = Names.instance(context).table;
}
/**
* 针对特定的java类
*
* @return
*/
@Override
public Set<String> getSupportedAnnotationTypes() {
HashSet<String> supportTypes = new LinkedHashSet<>();
supportTypes.add(EnableProbe.class.getCanonicalName());
return supportTypes;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.RELEASE_8;
}
/**
* 源代码修改入口
* 注意此方法会被调用多次
*
* @param annotations
* @param roundEnv
* @return
*/
@Override
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment roundEnv) {
if (!roundEnv.processingOver()) {
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(EnableProbe.class);
for (Element each : elements) {
if (each.getKind() != ElementKind.CLASS) {
continue;
}
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,
"handing probed class " + ((TypeElement) each).getQualifiedName().toString());
//修改java compiler的抽象语法树中的方法定义
JCTree tree = (JCTree) trees.getTree(each);
TreeTranslator visitor = new ProbeTranslator();
tree.accept(visitor);
}
} else {
//round结束后,还会调用所有的注解处理器一次
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, ">>> probe modify source code finished.");
}
//true 代表独占 false代表共享,会继续被其他注解处理器继续处理
return true;
}
private class ProbeTranslator extends TreeTranslator {
@Override
public void visitMethodDef(JCTree.JCMethodDecl jcMethodDecl) {
super.visitMethodDef(jcMethodDecl);
String methodName = jcMethodDecl.getName().toString();
if (methodName.equalsIgnoreCase("<init>")) {
//构造方法跳过
return;
}
JCTree.JCBlock body = jcMethodDecl.getBody();
JCTree.JCExpressionStatement startProbe = make.Exec(make.Apply(
List.of(memberAccess("java.lang.String")),
memberAccess("com.common.monitor.MonitorUtils.probe"),
List.of(make.Literal("[" + methodName + "] start. (Auto Generated)"))
));
List<JCTree.JCStatement> modifiedStatements = body.getStatements();
modifiedStatements = modifiedStatements.prepend(startProbe);
boolean hasReturn = modifiedStatements.stream().filter(x -> x instanceof JCTree.JCReturn).findAny().isPresent();
if (!hasReturn) {
JCTree.JCExpressionStatement endProbe = make.Exec(make.Apply(
List.of(memberAccess("java.lang.String")),
memberAccess("com.common.monitor.MonitorUtils.probe"),
List.of(make.Literal("[" + methodName + "] finished.(Auto Generated)(no return)"))
));
modifiedStatements = modifiedStatements.append(endProbe);
} else {
//改写每个return
JCTree.JCExpression returnType = jcMethodDecl.restype;
modifiedStatements = modifyReturn(returnType, modifiedStatements, methodName);
}
body.stats = modifiedStatements;
JCTree.JCMethodDecl methodDecl = make.MethodDef(jcMethodDecl.getModifiers(), jcMethodDecl.getName(),
jcMethodDecl.restype, jcMethodDecl.getTypeParameters(), jcMethodDecl.getParameters(), jcMethodDecl.getThrows(),
body, jcMethodDecl.defaultValue);
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "add probe finished. method=" + jcMethodDecl.getName());
result = methodDecl;
}
private boolean isVoid(JCTree.JCExpression jcExpression) {
if (jcExpression == null) {
return true;
}
if (!(jcExpression instanceof JCTree.JCPrimitiveTypeTree)) {
return false;
}
JCTree.JCPrimitiveTypeTree jcPrimitiveTypeTree = (JCTree.JCPrimitiveTypeTree) jcExpression;
return jcPrimitiveTypeTree.typetag == TypeTag.VOID;
}
/**
* 这里只改写了第一层,如果要完整改写,需要递归整个JCTree,对每一种 JCStatement都要处理
* 精力有限,并且一般第一层也够用了
*
* @param returnType
* @param statements
* @param methodName
* @return
*/
private List<JCTree.JCStatement> modifyReturn(JCTree.JCExpression returnType, List<JCTree.JCStatement> statements, String methodName) {
TreeMap<Integer, java.util.List<JCTree.JCStatement>> treeMap = new TreeMap<>();
for (int i = 0; i < statements.size(); ++i) {
if (statements.get(i) instanceof JCTree.JCReturn) {
treeMap.put(i, expandReturn(returnType, (JCTree.JCReturn) statements.get(i), methodName));
}
if (statements.get(i) instanceof JCTree.JCIf) {
}
}
if (treeMap.isEmpty()) {
return statements;
}
ArrayList<JCTree.JCStatement> result = new ArrayList<>();
for (int i = 0; i < statements.size(); ++i) {
if (statements.get(i) instanceof JCTree.JCReturn) {
result.addAll(treeMap.get(i));
} else {
result.add(statements.get(i));
}
}
return List.from(result);
}
private ArrayList<JCTree.JCStatement> expandReturn(JCTree.JCExpression returnType, JCTree.JCReturn jcReturn, String methodName) {
//拆成两个语句
JCTree.JCExpression expression = jcReturn.getExpression();
JCTree.JCVariableDecl resultDefinition = makeVarDef(make.Modifiers(0), "_probe_result", returnType, expression);
JCTree.JCReturn returnProbeResult = make.Return(make.Ident(names.fromString("_probe_result")));
JCTree.JCExpressionStatement endProbe = make.Exec(make.Apply(
List.of(memberAccess("java.lang.String")),
memberAccess("com.common.monitor.MonitorUtils.probe"),
List.of(make.Literal("[" + methodName + "] finished.(Auto Generated)(modify return)"))
));
if (isVoid(returnType)) {
return Lists.newArrayList(endProbe, jcReturn);
}
return Lists.newArrayList(resultDefinition, endProbe, returnProbeResult);
}
private JCTree.JCVariableDecl makeVarDef(JCTree.JCModifiers modifiers, String name, JCTree.JCExpression vartype, JCTree.JCExpression init) {
return make.VarDef(
modifiers,
names.fromString(name),
vartype,
init
);
}
private JCTree.JCExpression memberAccess(String components) {
String[] componentArray = components.split("\\.");
JCTree.JCExpression expr = make.Ident(names.fromString(componentArray[0]));
for (int i = 1; i < componentArray.length; i++) {
expr = make.Select(expr, names.fromString(componentArray[i]));
}
return expr;
}
@Override
public void visitImport(JCTree.JCImport jcImport) {
super.visitImport(jcImport);
}
}
}
java编译命令
javac -d /Users/zxl \
-classpath /Users/zhouxiliang/.m2/repository/zxl/org/zxl-demo/1.0-SNAPSHOT/zxl-demo-1.0-SNAPSHOT.jar \
-processor zxl.org.processor.ProbeProcessor \
-target 1.8 -source 1.8 -encoding UTF-8 -g -verbose Test.java
Demo版本的执行效果:
image.png image.pngMaven插件的激活方式
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<fork>true</fork>
<verbose>true</verbose>
<encoding>UTF-8</encoding>
<compilerArguments>
war
<sourcepath>
${project.basedir}/src/main/java
</sourcepath>
</compilerArguments>
<annotationProcessors>
<processor>
com.xxx.ProbeProcessor
</processor>
</annotationProcessors>
</configuration>
</plugin>
总结:基于注解的源代码修改方式是可行的,但是也有许多缺点,比如晦涩难懂,修改源代码后远程debug也会出现问题;知道原理即可,实际项目开发用到的还是比较少的;