当前位置: 首页 > news >正文

Java源码的前端编译

Java源码的前端编译


欢迎来到我的博客:TWind的博客

我的CSDN::Thanwind-CSDN博客

我的掘金:Thanwinde 的个人主页


0.前言

当一份Java代码写好时,将其进行编译,运行,并不是简单把这个Java源码从头到尾执行,一般来说会经历前端编译和后端编译两个阶段,前端编译会把Java源码进行分析,拆解,填充并进行一些优化来变成字节码

后端编译是发生在JVM已经在解释执行字节码时的JIT,会进行一些分析来将热点代码直接替换成大部分情况下效率更高的本地机器码,并进行许多优化,从而提高效率

除去前后编译,还有提前编译,后面都会写文章一一介绍


1.前端编译阶段

前端编译是一个Java源码的开始,在这里会将其转化为可以执行(效率不谈)的字节码

Javac

Javac是收录于JDK中的Java编译器。该工具可以将后缀名为.java的源文件编译为后缀名为.class的可以运行于JVM的字节码,Javac是由Java写的,运行javac的实质便是命令行的调用:javac hello.java

Java对于如何把.java文件转化为.class文件规范得很宽松,可能会导致class在一些JDK上能编译,一些却不行的情况

具体来说,Javac的编译过程可分为四个阶段

  • 初始化插入式注解处理器
  • 解析与填充符号表
    • 把源代码转化为标记集合来构造抽象语法树
    • 填充符号表
  • 注解处理器处理注解
  • 语义分析与生成字节码
    • 标注检查
    • 数据流与控制流分析
    • 解语法糖
    • 字节码生成

下面,让我们一个个来看:


初始化插入式注解处理器

JDK5之后,Java提供了对注解的支持,原本的方案是把注解作为运行时才发挥作用的,但是到了JDK6,引入了插入式注解处理器:让注解处理可以发生在编译阶段,一般来说,运行时的注解只能完成一些反射,自动化类的操作,但注解处理时可以动态的向其中加入代码:譬如lombok之类的动态生成代码,使注解能在代码层面影响代码

SPI

许多的框架其实都是依靠于这套机制完成的,而这些是基于一种名为 SPI 的技术

和SPI对应的是API,API(Application Programming Interface)

在这里插入图片描述

API就是上方:实现方已经实现了接口,要求调用方去实现它

SPI反过来:接口由调用方决定,实现方要根据调用方的接口要求去实现这个接口来提供服务

API最典型的实现就是OAuth2服务:你需要按照OAuth2提供商的要求去实现登录过程

SPI最典型的也就是JDBC,日志:各种数据库的JDBC驱动都是按照统一的接口规范来编写的,完全符合调用者制定的规范,众多的日志实现类也同理

但这只是SPI的概念,和Java中修改源代码的需求好像并无交集

具体来说,Java设计了许多接口,来供其他包作者去适配

这其中就包括注解的解释接口:javax.annotation.processing.AbstractProcessor,或是javax.annotation.processing.Processor(插入式注解处理器会放在META-INF/services之中)

在编译的时候,也就是一开始编译,Javac会先加载所有实现了这个接口的类,也就是所有插入式注解处理器(包括你自定义的),这实际上破坏了双亲委派模型

在这里插入图片描述

因为对于javac(对一般的java程序也一样)来说,这些插入式注解处理的实现(SPI)都属于“应用程序级”的,应该由最低级的应用程序类加载器加载

但实际上,他们的接口都位于“高层”,其接口是由启动类加载器加载的,但是这些高层的接口要去加载自己的实现类,就属于高级类加载器加载低级类加载器了

这里,java引入了线程上下文类加载器,这个加载器可以跨界进行加载,这样就能帮助高层的加载器加载底层的类

至于这里为什么强调“线程上下文”,我们可以以tomcat举例:通常里面有多个线程来处理请求,相互应该隔离:对于java,不同类加载器加载的同一个类,视为不同类,不相同也不兼容,这样就能隔离开不同线程

也不用担心每个线程都要去加载一个SPI,只会进行一次类加载,剩下的都会从缓存之中直接加载,尽管是其他线程

总而言之

通过SPI,我们就能解决Javac在准备编译时去加载插入式注解处理器的问题了


解析与填充符号表

词法语法分析

这里会把代码中的字符流转化为标记(Token),Token是编译时的最小元素,比如int a = b + 2,会拆分成6个Token:int ,a,=,b,+,2

接着会把这些Token按照顺序构造成抽象语法树(AST),其代表了一个程序的语法结构,你可以理解为是一个能保存一个程序的所有信息的数据结构

之后,Javac就不会再操作字符流了,一切都会围绕着语法树来进行

填充符号表

符号表类似于一个哈希表,用来标识每一个符号的地址和其信息,用人话来说的话,就是会标记一些Token,记录下这些Token的名字,类型,作用域等等,譬如方法名,变量名这类的,会用来进行各种检测,优化:比如语法检查,分配地址子类的,符号表就像是电话簿一样的角色


注解处理器处理注解

这里相对的简单:会调用每一个注解处理器进行处理,如果其对语法树进行了更改:就会回退到解析与填充符号表 重新处理,因为可能改变了语法以及符号表,这个操作称之为轮(Round),可以抽象成下面这个图

在这里插入图片描述

当所有注解处理器都处理完成后,便会进入下一个阶段:


语义分析与生成字节码

这里已经具有了一个完整的语法树和符号表,最后一步就是把这些转化成字节码了

标注检查

这里会进行类型赋值是否互相兼容,变量使用前是否声明,还会进行常量折叠,这是前端编译中为数不多的优化:int a = 1 + 2,这里会把1 + 2 在语法树上直接变成3

数据及控制流分析

这里会对各种逻辑进行进一步的验证,比如,对于局部变量用之前有无赋值,是否有返回值,异常是否会处理这种更复杂的控制

值得注意的是,这里的分析是和类加载期间的分析(运行时)基本相同,但存在一些东西只能在编译期或者运行时检测

就比如说局部变量的final,JVM对于字节码的要求是越短越好,对于局部变量的final关键字会直接被消除,那如何保证其值不会变?那就轮到编译器来判断了

为什么不会去掉成员变量的final?因为JVM 运行时有可能用到(比如常量折叠、只读约束等)

解语法糖

语法糖是一些用来帮助程序员进行编程的特殊语法,或许其不严谨或不规范但是其能大幅度提升程序员的幸福度

包括泛型(是的,Java匪夷所思没有真正的实现泛型,详见类型擦除),自动拆装箱,自动变长参数等,这些语法会在这个阶段被替换成最基础的语法

字节码生成

这里是最后一个阶段:把语法树,符号表转化成字节码

字节码的格式极其严格,这里会把其严格的转化成字节码,并向其中添加一些其他的代码,比如类构造器,实例构造器

变量初始化,调用父类构造等等

并还会添加一些优化,比如把字符串的+替换成StringBuffer的append之类的

到此,编译结束


现在已经生成了一份详尽且严谨的字节码,接下来一步就是开始解释执行,并开始最大的舞台:后端编译及优化

http://www.dtcms.com/a/269222.html

相关文章:

  • tomcat部署多个端口以及制定路径部署-vue3
  • Java创建型模式---原型模式
  • docker进入启动失败的容器
  • 图像处理中的边缘填充:原理与实践
  • AJAX vs axios vs fetch
  • [java: Cleaner]-一文述之
  • Python与Mongo数据库:下载安装mongodb与CompassGUI、python中安装monggo模块、如何在Mongo中插入一条数据
  • 10.5 实战ChatGLM3私有数据微调之提示工程:批量生成数据稳定性秘籍
  • HarmonyOS从入门到精通:自定义组件开发指南(二):组件属性与参数传递
  • Python实现MCP Server的完整Demo
  • 《声音的变形记:Web Audio API的实时特效法则》
  • Web 前端安全防护:防范常见攻击与漏洞的策略
  • SKUA-GOCAD入门教程-第八节 线的创建与编辑7和8
  • 特别放送:关于一个无法修复的系统级Bug
  • Eslint基础使用
  • 插入数据优化
  • 镜头OIS系统方案全面解析:从基础原理到前沿应用
  • 【ElasticSearch实用篇-01】需求分析和数据制造
  • Spring Cloud Config(微服务配置中心详解)
  • 七牛云Java开发面试题及参考答案(60道面试题汇总)
  • 华为OD机试 2025B卷 - 最小循环子数组(C++PythonJAVAJSC语言)
  • 【论文笔记】World Models for Autonomous Driving: An Initial Survey
  • 【C++读取输入空格到CHAR数组】2022-7-19
  • 在vue3+ts项目中引入element-plus及其图标
  • 【读代码】深度解析TEN VAD:实时语音活动检测的高性能开源解决方案
  • 从被动救火到主动预测!碧桂园服务以图谱技术重塑IT运维底座
  • 开放端口,开通数据库连接权限,无法连接远程数据库 解决方案
  • Debian 11 Bullseye 在线安装docker
  • Java 命令行参数详解:系统属性、JVM 选项与应用配置
  • axios无感刷新token