当前位置: 首页>后端>正文

javac 编译源文件

看这个有什么用:
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查看


https://www.xamrdz.com/backend/3pk1944796.html

相关文章: