与.project文件说“再见”——VSCodeJava1.1.0背后的故事文件(.project、.classpath、settings等)将不再默认生成在项目路径中。从2018年开始,这个问题已经记录了三年多了。本文旨在记录和分享我们解决这个问题的过程和最终的解决方案。悬在头顶的“达摩克利斯之剑”随着VSCodeJava的功能逐渐丰富,用户数量也在稳步增长。但是由于Java插件在导入项目时会在项目目录下生成元数据文件的问题,我们收到了很多1星的差评。可以预见,随着用户基数的增加,因该问题导致的差评数量也会增加。这就像悬在我们头上的“达摩克利斯之剑”。如果不及时解决,问题随时可能爆发。其实这并不是因为我们的产品团队不想彻底解决这个问题。根源需要从Java语言服务的架构说起:JDTJavaLanguageServer架构示意图红帽。从上面的项目架构图中可以看出,我们在实现中复用了Eclipse的一些模块,这些自动生成的元数据文件也是由上游的一些模块生成的。可以在Eclipse的讨论区中找到相关的讨论线程。这个帖子的创建甚至可以追溯到2004年,由于在实现的时候这些元数据文件的路径已经硬编码在代码中作为常量,而这些常量又被各种Eclipse模块甚至插件所引用,所以这个问题就变成了多年来在某种意义上是一个问题。“历史包袱”。考虑到改变上游模块的行为涉及到太多的未知和不确定性,过去我们尝试为用户提供一些变通方案,比如让这些元数据文件隐藏在VSCode的文件浏览器中,并引导用户将它们添加到.gitignore。但从用户反馈来看,这些方式并没有让用户满意。为了彻底解决这个困扰我们和用户三年多的“病”,我们决定在今年下半年再次尝试,希望能“治愈”它。解决方案一、使用SymbolicLink(失败)我们首先想到的方法是使用SymbolicLink。在导入项目时,可以通过SymbolicLink将导入的项目链接到用户看不到的地方,从而在链接路径下生成元数据文件。但是很快这个方案遇到了一个问题——在某些操作系统下创建SymbolicLink需要特定的权限,否则会抛出FileSystemException,这显然不是我们想要的效果,所以这个方案马上就被否决了。Solution2.UsingEclipseLinkedResources(abandoned)和SymbolicLink思路类似,我们也可以选择使用EclipseLinkedResources:LinkedResources:Linkedresources是文件系统中存储在项目外部的文件和文件夹地点。以上是LinkedResources的官方定义,可以作为项目的一部分使用,但允许存放在项目路径之外的其他位置。在VSCodeJava中,我们通过UnmanagedFolder(没有构建系统的项目)的LinkedResources机制隐藏这些元数据文件。其实现原理如下图所示:UnmanagedFolder实现原理可以看实际路径放在LanguageServer工作区存储中。用户通常不知道这条路径。同时,在.project文件中,我们定义了LinkedResources的目标路径,即用户在VSCode中打开的文件夹位置。它作为项目的一部分,会像其他项目一样参与建设过程,其开发经历也是类似的。同样的原理可以应用到Maven项目和Gradle项目的导入过程中来解决这个问题,所以我们在M2E模块上进行了一些实验。M2E模块负责Java语言服务中Maven项目的导入。通过更改模块中的相关代码,使用LinkedResources机制,可以将元数据文件生成到项目路径之外的地方。最终的实验结果是可行的,但是这个方案的缺点也很明显:改动大:需要改动的代码分散在整个模块的不同文件中(十几个左右),而且由于大代码大小,没有办法确定这些更改是否在短时间内完成。对下游模块不透明:因为多了一层LinkedFolder,这会让Java项目视图在显示项目结构时多显示一层代表LinkedFolder的目录结构。Java项目视图的实现需要增加一些额外的控制逻辑,使项目结构的显示与普通项目相同。可行性未知:Maven和Gradle构建系统的支撑模块M2E和Buildship都是上游项目,所以这个概念是否会被接受还未知。可扩展性差:如果要支持新的构建系统,需要重新实现类似的逻辑。考虑到以上原因,团队经过讨论决定暂时放弃EclipseLinkedResources方案,继续寻找更好的方案。我找到了“银弹”放弃第二种解决方案的另一个原因:Eclipse已经发布20年了。在保证稳定运行的同时,可以不断增加新的功能,提供优秀的扩展能力。包含优秀的架构设计和可扩展性。直觉上,我们觉得应该有更优雅的解决方案。因此,这次我们直接分析了Eclipse的底层文件系统,最终找到了解决问题的“银弹”:FileSystemProvider和FileStore(注:虽然在软件工程领域,人们的共识是没有灵丹妙药,我们确实找到了一个有点“KatKat”的解决方案来解决这个特定问题)。Eclipse工作空间结构和FileStoreEclipse在运行过程中会为整个工作空间维护一个树形结构。树的节点代表文件系统中的文件或目录,也保存了文件的一些重要信息,如修改时间等。Eclipse底层通过FileStore类将这些节点与文件系统中的文件相关联。FileStore类还有一个重要的特点:如果映射的对象是单个文件,那么FileStore也会负责提供这个文件的输入输出流。这个特性为问题的解决带来了一个非常重要的思路:只要能够将元数据文件的输入输出流重定向到项目目录之外的位置,就可能解决问题。有了这个假设,我们又发现了另一个关键线索:FileSystemProvider。方案三、FileSystemProviderFileSystemProvider是Eclipse平台对外开放的扩展点,允许开发者实现一个Eclipse文件系统接口(org.eclipse.core.filesystem.IFileSystem),并注册到扩展点,用于处理具有特定URI方案的文件请求。所以我们从FileSystemProvider的扩展点入手,继承并重写Eclipse默认对URIscheme为file的文件系统的处理,通过重写部分方法,让文件系统在处理元数据文件时重定向文件路径读取和写入项目路径之外的位置。与方案二相比,该方案的优势在于对其他模块完全透明,无需修改即可正常工作,这也意味着更好的扩展性。代码量非常少,最终的实现,包括JavaDoc和注释,总共只有300行左右。当然,这个方案并不完美,因为它需要其他模块通过Eclipse提供的API来读写元数据文件。在实现过程中,我们发现上游Buildship在处理元数据文件时,直接通过JDK中的文件I/OAPI进行读写。因此,我们提交了变更请求,将相关操作迁移到EclipseAPI。总结权衡利弊后,我们最终选择了第三种方案,解决了这个困扰VSCodeJava用户三年多的问题。虽然最后的实现并不复杂,但是寻找答案的过程却非常戏剧化。最后,特别感谢EclipsePlatform项目成员MickaelIstria和AlexanderFedorov。在问题的讨论过程中,他们给出了非常有用的建议,对解决问题起到了非常关键的作用。反馈与建议:请积极使用我们的产品!您的反馈和建议对我们非常重要,将帮助我们做得更好。有几种方法可以给我们留下反馈填写中文问卷https://www.research.net/r/vs...在此主题下发表评论在我们的GitHub存储库https://www.research上创建一个问题。net/r/vs...资源:以下链接和资源可以帮助您更好地理解VisualStudioCode上的Java。在VisualStudioCodehttps://code.visualstudio.com上了解有关Java的更多信息。..一步步探索关于VisualStudioCode的Java教程https://code.visualstudio.com...欢迎关注微软中国MSDN订阅号获取更多最新版本!
