百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分析 > 正文

和 .project 文件说“再见”——VS Code Java 1.1.0 背后的故事

liebian365 2024-11-11 14:45 21 浏览 0 评论

Language Support for Java 1.1.0 版本包含了一项重要更新:现在插件在导入新的 Java 项目时,项目元数据文件(.project,.classpath,settings等)默认将不再生成于项目路径下。这一问题 自 2018 年被记录至今已有超过三年的时间。本文旨在记录并分享我们解决这一问题的过程和最后的解决方案。

Language Support for Java

https://marketplace.visualstudio.com/items?itemName=redhat.java

悬在头顶的“达摩克利斯之剑”

随着 VS Code Java 的功能逐渐丰富,用户数量也在稳步上升。但是由于 Java 插件在导入项目时,会在项目目录下生成元数据文件的问题,我们得到了不少的1星差评。可以预见,随着用户基数增加,因这一问题而造成的差评数量也会增加。这就如同一把悬在我们头顶的“达摩克利斯之剑”,如果不及时解决,问题随时都有可能爆发。

其实这并不是我们产品组不想彻底修复这一问题,根本原因需要从 Java 语言服务的架构说起:

JDT Java Language Server 架构示意图

VS Code Java 项目背后所采用的 Java 语言服务的正式项目名称是 Eclipse JDT Language Server?,由微软和红帽联手开发。在上面的项目架构图中可以看到,我们在实现中复用了 Eclipse 的一些模块,而这些自动生成的元数据文件也正是由其中一些上游模块所产生。在 Eclipse 的讨论区中可以找到一条相关的讨论帖子。这条帖子的创建时间甚至可以追溯到2004年。由于在实现时,这些元数据文件的路径就已经作为常量被硬编码在了代码里,这些常量又被各个不同的 Eclipse 模块甚至是插件引用,经年累月下来这一问题从某种意义上已经成为了“历史包袱”。

考虑到改变上游模块的行为包含了太多的未知和不确定性,在过去我们尝试给用户提供一些变通方法,比如让这些元数据文件在 VS Code 的文件浏览器中隐藏,并引导用户将他们添加至 .gitignore 当中。但从用户的反馈来看,这些方式并没有让用户感到满意。为了能够彻底解决这个已经困扰了我们以及用户三年多之久的“顽疾”,我们在今年下半年决定再做一次尝试,希望能将其“根治”。

Eclipse JDT Language Server?

https://projects.eclipse.org/projects/eclipse.jdt.ls

帖子

https://bugs.eclipse.org/bugs/show_bug.cgi?id=78438

方案一:使用 Symbolic Link(失败)

我们最先想到的方法是使用 Symbolic Link。在导入项目时,可以将被导入的项目通过 Symbolic Link 的方式链接到一个用户看不到的地方,从而让元数据文件生成在链接后的路径下。但很快这一方案就遇到了问题——在某些操作系统下创建 Symbolic Link 需要特定的权限,否则会抛出 FileSystemException,这显然不是我们想要的效果,因此这个方案马上被否决了。

Symbolic Link

https://en.wikipedia.org/wiki/Symbolic_link

方案二:使用 Eclipse Linked Resources(放弃)

和 Symbolic Link 的思路类似地,我们还可以选择使用 Eclipse Linked Resources:

Linked Resources: Linked resources are files and folders that are stored in locations in the file system outside of the project's location.

上文是 Linked Resources 的一段官方定义,它可以作为项目的一部分,但又允许存储在项目路径之外的其他位置。在 VS Code Java 中,我们对于 Unmanaged Folder(无构建系统的项目),就是通过 Linked Resources 机制将这些元数据文件隐藏的,它的实现原理如下图所示:

Unmanaged Folder 实现原理

可以看到项目的实际路径放在了 Language Server workspace storage 中,用户通常并不知晓这一路径,同时在 .project 文件里我们定义了 Linked Resources 的目标路径,也就是用户在 VS Code 打开的文件夹位置,它作为项目的一部分,会像其他项目一样参与到构建过程当中,其开发体验是类似的。

相同的原理可以应用到 Maven 项目和 Gradle 项目的导入过程当中来解决这一问题,因此,我们在 M2E 模块上进行了一些实验。M2E 模块在 Java 语言服务中负责 Maven 项目的导入,通过改动模块中的相关代码,并利用 Linked Resources 机制就可以将元数据文件生成到项目路径之外的地方。

最终的实验结果是可行的,但是这套方案的缺点也非常明显:

  • 改动较大:需要改动的代码散落在整个模块的不同文件中(大约十几处),同时因为代码规模较大,没有办法在短时间内确定这些改动是否是完备的。
  • 对下游模块不透明:因为多了一层 Linked Folder,这会让 Java 项目视图在展示项目结构时,多出一层代表了 Linked Folder 的目录结构。在 Java 项目视图的实现中需要增加一些额外的控制逻辑,让项目结构的展示和正常项目一样。
  • 可行性未知:对于 Maven 和 Gradle 构建系统的支持模块 M2E 和 Buildship 都是上游项目,这一概念能否被采纳接受是个未知数。
  • 扩展性差:如果要支持一套新的构建系统,需要将类似的逻辑再实现一遍。

考虑到上述原因,团队在经过讨论之后决定暂时放弃 Eclipse Linked Resources 方案,并继续寻找更优的解决办法。

发现“银弹”

放弃第二套方案还有另一个原因:Eclipse 自发布至今二十载,在保证稳定运行的同时,可以不断地增加新的功能且提供了出色的拓展能力,在这背后一定蕴含了优秀的架构设计和可拓展性。直觉上让我们觉得应该还会有更优雅的解决办法。

因此,这一次我们直接从 Eclipse 底层文件系统入手分析,并最终发现了一枚解决问题的“银弹”:File System Provider 和 FileStore(注:虽然在软件工程领域,人们的共识是没有银弹,不过对于这一特定的问题,我们确实找到了一种比较“奇巧”的解决办法)。

Eclipse 工作空间结构与 FileStore

Eclipse 在运行过程中会为整个工作空间维护一颗树形结构,树的节点代表了文件系统中的文件或目录,同时还保存了文件的一些重要信息,如修改时间等。

Eclipse 底层通过 FileStore 类将这些节点和文件系统中的文件进行关联。FileStore 类还有一个重要特性:如果映射的对象是单个文件,那么 FileStore 还会负责提供这一文件的输入输出流。

这一特性为问题的解决带来了非常重要的思路:只要能够将元数据文件的输入输出流重定向到项目目录之外的位置,问题也许就能得以解决。带着这个假设,我们又发现了另一个关键线索:File System Provider。

方案三:File System Provider

File System Provider 是 Eclipse 平台对外开放的一个扩展点,它允许开发人员实现一个 Eclipse 文件系统接口(org.eclipse.core.filesystem.IFileSystem),并将其注册到扩展点上,用以处理具有特定 URI scheme 的文件请求。

于是我们从 File System Provider 这一拓展点入手,继承并覆盖了 Eclipse 默认处理 URI scheme 为 file 的文件系统,通过覆写其中的一些方法,让文件系统在处理元数据文件时,将文件路径重定向到项目路径之外的地方进行读写。相比于方案二,这一套方案的优点在于:

  • 对其他模块完全透明,基本不需要进行修改就能正常工作,这同时还意味着较好的拓展性。
  • 代码量很小,最终的实现,算上 JavaDoc 和注释,一共只有 300 行左右。eclipse.jdt.ls/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/filesystem at v1.6.0 · eclipse/eclipse.jdt.ls · GitHub

当然这个方案也并非完美,因为它要求其他模块通过 Eclipse 提供的 API 进行对元数据文件的读写操作。我们在实现过程中就发现上游 Buildship 在处理元数据文件时直接通过 JDK 中的文件 I/O API 进行读写,为此我们提交了一份变更请求将相关操作迁移到了 Eclipse API 上。

处理元数据文件时直接通过 JDK 中的文件 I/O API 进行读写

https://github.com/eclipse/buildship/issues/1111

变更请求

https://github.com/eclipse/buildship/pull/1114

总结

在权衡了利弊之后,我们最终选取了第三套方案并解决了这一困扰了 VS Code Java 用户三年多时间的问题。虽然最终的实现并不复杂,但探寻答案的过程却非常具有戏剧性。


最后特别感谢 Eclipse Platform 项目成员 Mickael Istria 以及 Alexander Fedorov。在问题讨论的过程中他们给予了非常有用的建议,对问题的解决起到了非常关键的作用。

相关推荐

4万多吨豪华游轮遇险 竟是因为这个原因……

(观察者网讯)4.7万吨豪华游轮搁浅,竟是因为油量太低?据观察者网此前报道,挪威游轮“维京天空”号上周六(23日)在挪威近海发生引擎故障搁浅。船上载有1300多人,其中28人受伤住院。经过数天的调...

“菜鸟黑客”必用兵器之“渗透测试篇二”

"菜鸟黑客"必用兵器之"渗透测试篇二"上篇文章主要针对伙伴们对"渗透测试"应该如何学习?"渗透测试"的基本流程?本篇文章继续上次的分享,接着介绍一下黑客们常用的渗透测试工具有哪些?以及用实验环境让大家...

科幻春晚丨《震动羽翼说“Hello”》两万年星间飞行,探测器对地球的最终告白

作者|藤井太洋译者|祝力新【编者按】2021年科幻春晚的最后一篇小说,来自大家喜爱的日本科幻作家藤井太洋。小说将视角放在一颗太空探测器上,延续了他一贯的浪漫风格。...

麦子陪你做作业(二):KEGG通路数据库的正确打开姿势

作者:麦子KEGG是通路数据库中最庞大的,涵盖基因组网络信息,主要注释基因的功能和调控关系。当我们选到了合适的候选分子,单变量研究也已做完,接着研究机制的时便可使用到它。你需要了解你的分子目前已有哪些...

知存科技王绍迪:突破存储墙瓶颈,详解存算一体架构优势

智东西(公众号:zhidxcom)编辑|韦世玮智东西6月5日消息,近日,在落幕不久的GTIC2021嵌入式AI创新峰会上,知存科技CEO王绍迪博士以《存算一体AI芯片:AIoT设备的算力新选择》...

每日新闻播报(September 14)_每日新闻播报英文

AnOscarstatuestandscoveredwithplasticduringpreparationsleadinguptothe87thAcademyAward...

香港新巴城巴开放实时到站数据 供科技界研发使用

中新网3月22日电据香港《明报》报道,香港特区政府致力推动智慧城市,鼓励公私营机构开放数据,以便科技界研发使用。香港运输署21日与新巴及城巴(两巴)公司签署谅解备忘录,两巴将于2019年第3季度,开...

5款不容错过的APP: Red Bull Alert,Flipagram,WifiMapper

本周有不少非常出色的app推出,鸵鸟电台做了一个小合集。亮相本周榜单的有WifiMapper's安卓版的app,其中包含了RedBull的一款新型闹钟,还有一款可爱的怪物主题益智游戏。一起来看看我...

Qt动画效果展示_qt显示图片

今天在这篇博文中,主要实践Qt动画,做一个实例来讲解Qt动画使用,其界面如下图所示(由于没有录制为gif动画图片,所以请各位下载查看效果):该程序使用应用程序单窗口,主窗口继承于QMainWindow...

如何从0到1设计实现一门自己的脚本语言

作者:dong...

三年级语文上册 仿写句子 需要的直接下载打印吧

描写秋天的好句好段1.秋天来了,山野变成了美丽的图画。苹果露出红红的脸庞,梨树挂起金黄的灯笼,高粱举起了燃烧的火把。大雁在天空一会儿写“人”字,一会儿写“一”字。2.花园里,菊花争奇斗艳,红的似火,粉...

C++|那些一看就很简洁、优雅、经典的小代码段

目录0等概率随机洗牌:1大小写转换2字符串复制...

二年级上册语文必考句子仿写,家长打印,孩子照着练

二年级上册语文必考句子仿写,家长打印,孩子照着练。具体如下:...

一年级语文上 句子专项练习(可打印)

...

亲自上阵!C++ 大佬深度“剧透”:C++26 将如何在代码生成上对抗 Rust?

...

取消回复欢迎 发表评论: