看这个有什么用:
1.是否在面试时遇到过类中属性的加载顺序,比如static属性和代码块的先后顺序,通过了解java编译过程能直达痛处
2.lombok咋生效的
jdk编译器使用java代码来编译
解析主函数代码位置src/jdk.compiler/share/classes/com/sun/tools/javac/main/Main.java
读取代码后解析为JCTree,理解为代码树
树中上层包含类描述,引用等,类中包含函数method与字段variable描述,函数其下包含函数体
类描述中包含了代码位置,初始值,异常抛出等信息
相关处理概念:
顶层类(Top-Level Class),是 Java 中对类的一种定义方式。在 .java 文件中,处于最外层的类就称为顶层类,在其外部不存在将其包围起来的任何代码块。
在编译过程中会将编译的java类汇集成一个树,树的根就是外层类
看源码时发现方法解析时可见最大方法参数数量为255包含所有默认的参数
最后在代码被解析成字节码翻译成16进制后ClassWriter 将解析的类写到class文件中
在解析过程中所有类会默认添加无参构造init方法
如果包含了静态代码块或者静态属性需要赋初值,才会增加clinit方法
List<JCTree> normalizeDefs(List<JCTree> defs, Symbol.ClassSymbol c) {
ListBuffer<JCTree.JCStatement> initCode = new ListBuffer<>();
ListBuffer<Attribute.TypeCompound> initTAs = new ListBuffer<>();
ListBuffer<JCTree.JCStatement> clinitCode = new ListBuffer<>();
ListBuffer<Attribute.TypeCompound> clinitTAs = new ListBuffer<>();
ListBuffer<JCTree> methodDefs = new ListBuffer<>();
for (List<JCTree> l = defs; l.nonEmpty(); l = l.tail) {
JCTree def = l.head;
switch (def.getTag()) {
//有代码块,如果是static就加clinit,不是就加init
case BLOCK:
JCTree.JCBlock block = (JCTree.JCBlock)def;
if ((block.flags & STATIC) != 0)
clinitCode.append(block);
else if ((block.flags & SYNTHETIC) == 0)
initCode.append(block);
break;
case METHODDEF:
methodDefs.append(def);
break;
case VARDEF:
JCTree.JCVariableDecl vdef = (JCTree.JCVariableDecl) def;
Symbol.VarSymbol sym = vdef.sym;
checkDimension(vdef.pos(), sym.type);
if (vdef.init != null) {
if ((sym.flags() & STATIC) == 0) {
//final常量走这一段
JCTree.JCStatement init = make.at(vdef.pos()).
Assignment(sym, vdef.init);
initCode.append(init);
endPosTable.replaceTree(vdef, init);
initTAs.addAll(getAndRemoveNonFieldTAs(sym));
} else if (sym.getConstValue() == null) {
JCTree.JCStatement init = make.at(vdef.pos).
Assignment(sym, vdef.init);
// 静态参数有初始值但未赋值 Initialize class (static) variables only if
// 并不是final常量 they are not compile-time constants.
clinitCode.append(init);
endPosTable.replaceTree(vdef, init);
clinitTAs.addAll(getAndRemoveNonFieldTAs(sym));
} else {
checkStringConstant(vdef.init.pos(), sym.getConstValue());
vdef.init.accept(classReferenceVisitor);
}
}
break;
default:
Assert.error();
}
}
if (initCode.length() != 0) {
List<JCTree.JCStatement> inits = initCode.toList();
initTAs.addAll(c.getInitTypeAttributes());
List<Attribute.TypeCompound> initTAlist = initTAs.toList();
for (JCTree t : methodDefs) {
normalizeMethod((JCTree.JCMethodDecl)t, inits, initTAlist);
}
}
if (clinitCode.length() != 0) {
Symbol.MethodSymbol clinit = new Symbol.MethodSymbol(
STATIC | (c.flags() & STRICTFP),
names.clinit,
new Type.MethodType(
List.nil(), syms.voidType,
List.nil(), syms.methodClass),
c);
c.members().enter(clinit);
List<JCTree.JCStatement> clinitStats = clinitCode.toList();
JCTree.JCBlock block = make.at(clinitStats.head.pos()).Block(0, clinitStats);
block.endpos = TreeInfo.endPos(clinitStats.last());
methodDefs.append(make.MethodDef(clinit, block));
if (!clinitTAs.isEmpty())
clinit.appendUniqueTypeAttributes(clinitTAs.toList());
if (!c.getClassInitTypeAttributes().isEmpty())
clinit.appendUniqueTypeAttributes(c.getClassInitTypeAttributes());
}
return methodDefs.toList();
}
}
代码生成
逐行解析代码后生成了语法树
comp.compile(args.getFileObjects(), args.getClassNames(), null, List.nil());
其中的generate(desugar(flow(attribute(todo.remove()))));
attribute对变量进行标注
flow会根据数据流判断变量使用状态,看是否初始化、final被重新赋值等
desugar会优化代码解析语法糖
代码映射
所有方法调用通过访问者模式进行树形结构处理,java里面的每一个函数都会有一个对应的数字符号作为替代
com.sun.tools.javac.jvm.ByteCodes 中包含了指令的代码
方法体中所有方法都是执行的指令加上代码member的引用
所有代码的ascii码转换成16进制保存到class文件中
用法我们比如说,lombok的处理过程
在这里会调用lombok的注解实现类处理注解,在class描述中添加对应getter/setter等
try {
annotationProcessingOccurred =
procEnvImpl.doProcessing(roots,
classSymbols,
pckSymbols,
deferredDiagnosticHandler);
// doProcessing will have handled deferred diagnostics
} finally {
procEnvImpl.close();
}
进入procEnvImpl向下追源码, 来到round.run(false, false);
void run(boolean lastRound, boolean errorStatus) {
printRoundInfo(lastRound);
if (!taskListener.isEmpty())
taskListener.started(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND));
try {
if (lastRound) {
filer.setLastRound(true);
Set<Element> emptyRootElements = Collections.emptySet(); // immutable
RoundEnvironment renv = new JavacRoundEnvironment(true,
errorStatus,
emptyRootElements,
JavacProcessingEnvironment.this);
discoveredProcs.iterator().runContributingProcs(renv);
} else {
//这里就是能看到@Data注解
discoverAndRunProcs(annotationsPresent, topLevelClasses, packageInfoFiles, moduleInfoFiles);
}
} catch (Throwable t) {
// we're specifically expecting Abort here, but if any Throwable
// comes by, we should flush all deferred diagnostics, rather than
// drop them on the ground.
deferredDiagnosticHandler.reportDeferredDiagnostics();
log.popDiagnosticHandler(deferredDiagnosticHandler);
compiler.setDeferredDiagnosticHandler(null);
throw t;
} finally {
if (!taskListener.isEmpty())
taskListener.finished(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND));
}
}
再进入 discoverAndRunProcs(annotationsPresent, topLevelClasses, packageInfoFiles, moduleInfoFiles);
while(unmatchedAnnotations.size() > 0 && psi.hasNext() ) {
ProcessorState ps = psi.next();
Set<String> matchedNames = new HashSet<>();
Set<TypeElement> typeElements = new LinkedHashSet<>();
//找对应注解@Data的处理器,找到了才会进行解析
for (Map.Entry<String, TypeElement> entry: unmatchedAnnotations.entrySet()) {
String unmatchedAnnotationName = entry.getKey();
if (ps.annotationSupported(unmatchedAnnotationName) ) {
matchedNames.add(unmatchedAnnotationName);
TypeElement te = entry.getValue();
if (te != null)
typeElements.add(te);
}
}
if (matchedNames.size() > 0 || ps.contributed) {
//处理器工作
boolean processingResult = callProcessor(ps.processor, typeElements, renv);
ps.contributed = true;
ps.removeSupportedOptions(unmatchedProcessorOptions);
if (printProcessorInfo || verbose) {
log.printLines("x.print.processor.info",
ps.processor.getClass().getName(),
matchedNames.toString(),
processingResult);
}
if (processingResult) {
unmatchedAnnotations.keySet().removeAll(matchedNames);
}
}
}
再往下看处理器实现的地方
public class AnnotationProcessor extends AbstractProcessor
这里就是Lombok的实现,具体可以debug查看