和 .project 文件说“再见”—— VS Code Java 1.1.0 背后的故事_java 微软project-程序员宅基地

技术标签: visual studio code  Java  微软产品速递  

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

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

随着 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 当中。但从用户的反馈来看,这些方式并没有让用户感到满意。为了能够彻底解决这个已经困扰了我们以及用户三年多之久的“顽疾”,我们在今年下半年决定再做一次尝试,希望能将其“根治”。

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

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

方案二、使用 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 提供的 API 进行对元数据文件的读写操作。我们在实现过程中就发现上游 Buildship 在处理元数据文件时直接通过 JDK 中的文件 I/O API 进行读写,为此我们提交了一份变更请求将相关操作迁移到了 Eclipse API 上。

总结

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

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

反馈与建议:

请积极使用我们的产品!您的反馈和建议对我们非常重要,并将帮助我们做得更好。有几种方法可以给我们留下反馈

  • 填写中文问卷

    https://www.research.net/r/vscodejava?lang=zh

  • 在这个帖子下留言

  • 在我们的GitHub repo上创建Issue

    https://www.research.net/r/vscodejava?lang=zh

资源:

以下链接和资源能帮助您更好地了解Java on Visual Studio Code的相关信息

  • 了解更多 Visual Studio Code上Java的更多信息

    https://code.visualstudio.com/docs/languages/java

  • 逐步探索有关Visual Studio Code的Java教程

    https://code.visualstudio.com/docs/java/java-tutorial

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/helendemeng/article/details/121692180

智能推荐

25.hadoop系列之Yarn Tool接口实现动态传参_yarn脚本后缀传值-程序员宅基地

文章浏览阅读139次。当我们通过yarn向集群提交自定义任务时,若传入队列参数会报错此时我们需要Tool工具,进行动态传参控制。_yarn脚本后缀传值

Android Studio查看Android源码_win查看安卓源码-程序员宅基地

文章浏览阅读4.3k次。最近在折腾面试,然后就有些组件的原理需要看源码,之前也弄过,都是用的Source Insight在win10里面搞的,今天试了一下Mac,源码60G,我这个版本是Android 11,目前是最新版的代码。用AS看源码首先要构建项目配置文件,就是ipr和iml工具,用到Android源码里面的idegen工具,但是你用官方的mmm命令方法会报错,说文件格式不对,本来是可以用mmm命令直接生成的,但是Mac的文件系统跟官方要求的有问题,所以就放弃了,有想试试的同学可以执行下面的命令。cd ~/aosp_win查看安卓源码

使用正则表达式判断URL地址是否合法_判断是否是正常链接正则表达式-程序员宅基地

文章浏览阅读1k次。在编程中,我们常常需要对给定的URL地址进行合法性检测。函数来判断URL地址是否合法。该函数接受一个URL地址作为参数,并返回一个布尔值,表示该URL地址是否合法。总之,在Python中使用正则表达式判断URL地址是否合法非常简单,只需要编写一个正则表达式,然后使用。在函数内部,我们使用正则表达式来匹配URL地址的各个部分,并将它们拼接在一起。函数来判断该URL地址是否符合我们定义的正则表达式规则。匹配URL地址的路径部分。在示例代码中,我们将一个合法的URL地址。使用正则表达式判断URL地址是否合法。_判断是否是正常链接正则表达式

写一个基于知识图谱的古诗词问答系统的背景意义,500字-程序员宅基地

文章浏览阅读260次。古诗词是中国文化的重要组成部分,具有悠久的历史和独特的艺术价值。但是,由于其语言难度较高、时代背景和文化背景的复杂性等原因,使得普通读者很难深入理解古诗词的内涵和价值。此时,基于知识图谱的古诗词问答系统应运而生,其背景意义包括以下几个方面:一、促进文化传承古诗词是中华文化的瑰宝,它们承载了丰富的历史文化信息。而传统的古诗词学习方式主要依靠课堂教学和自学,但这种学习方式往往难以满足大众的需求。基..._古诗问答系统

MySQL自动备份脚本_mysqldump 脚本-程序员宅基地

文章浏览阅读660次,点赞10次,收藏10次。mysqldump命令将数据库中的数据备份成一个文本文件,表的结构和数据将存储在生成的文本文件中。将备份出来的数据还原到某个数据库中。备份一个数据库下的多个表。MySQL自动备份脚本。也可以登入数据之后进行。_mysqldump 脚本

程序员刚写完代码,就被开除了_程序员需求写不出来被开除-程序员宅基地

文章浏览阅读1.1k次。学小编逛贴吧发现一个热帖,就凑了凑热闹~~~看到这样一个帖。一程序员说自己刚写完代码,就别公司老板给开除了。为什么会这样的呢?原来是这位程序员写了一段这样的代码:public static Date getNextDay() {try {Thread.sleep(246060*1000);} catch (InterruptrdException e) {e.printStackTr..._程序员需求写不出来被开除

随便推点

rpm常用命令记录_rpm命令-程序员宅基地

文章浏览阅读1.7k次。linux - rpm _rpm命令

ssm小学生课外知识学习网站+vue-程序员宅基地

文章浏览阅读26次。用户可以在首页访问小学生课外知识学习网站方面信息,首页上面有导航栏,导航栏上面有课外知识,试卷列表,学习资讯,个人中心,后台管理等,点击导航栏课外知识可以看到很多信息,点击某个信息可以查看对应详情信息,用户登录后可以对小学生课外知识学习网站文章信息进行评论操作。管理员可以对课外知识进行添加,查询,修改,删除操作。下图就是课外知识管理页面。管理员可以对知识分类信息进行删除,查询和修改操作。前端技术:JavaScript、VUE.js(2.X)、css3。数据库管理工具:Navicat或sqlyog。

goland 常用快捷键_goland进入ctrl+b函数后,什么快捷键返回上一个函数-程序员宅基地

文章浏览阅读1.7w次,点赞3次,收藏9次。转自:https://www.cnblogs.com/zhishuai/p/7942273.htmljetbrains goland 跳到上一个光标处查了下是 :Ctrl + Alt + 左右 mac下面是:Command+ Alt + 左右键但是我用下来是切上面打开文档页摸索了下是:Ctrl +Win+ Alt + 左右 我的键的映射是Default 。另外jb确实也..._goland进入ctrl+b函数后,什么快捷键返回上一个函数

Python3-word文档操作(六):word文档中表格的操作-单元格文字居中,字体颜色等的设置_doc.tables[0].cell(a, b).vertical_alignment = wd_a-程序员宅基地

文章浏览阅读4.6k次,点赞3次,收藏14次。用python3操作word文档,实现:word文档中表格的操作-单元格文字居中,字体颜色等的设置。python设置文档字体,python设置文档字体颜色,python设置单元格文字居中。_doc.tables[0].cell(a, b).vertical_alignment = wd_align_vertical.center # 垂

Ubuntu系统使用技巧 Vim基本技巧介绍_ubuntu系统vim操作-程序员宅基地

文章浏览阅读231次。Ubuntu系统使用技巧: Ctrl+alt+t: 打开终端​ Ctrl+L:清理屏幕​ Tab: 自动补全​ up/down: 调出命令执行记录​ Ctrl+Alt+方向键:切换工作区​ Alt+Tab: 切换任务重定向、管道、通配符:重定向:就是把命令的执行结果写入到文件中​ cmd > filename 把命令的执行结果以清空的方式写入到文件中(先清空再写入)​ cmd >> filename 把命令的执行结果以追加的方式写入到文件中管道:_ubuntu系统vim操作

Nacos 使用指南-程序员宅基地

文章浏览阅读420次,点赞3次,收藏8次。123。

推荐文章

热门文章

相关标签