在Gradle中,如何仅包含库中的特定类,而不是添加整个依赖项?
我在做一个项目,只需要第三方库中的少量特定类。但是,当我在Gradle中添加依赖时,它包含了整个库。有没有办法在Gradle中只导入或包含库的某些特定类或部分,而不是整个依赖?
解决方案
如果它们是具体的小类(例如文本处理相关的类),你可能应该直接把它们拷贝下来。
通常会有功能极其丰富的库。要不要实际包含它们,取决于你。
使用这个库,你的代码会更稳定、经过更多测试、投入也更充分,只需简单地点击/引入即可。
然而,你得到的是库的全部内容,数量往往相当大(看着你,Guava... ;))。
不仅会增加体积(可能还会带来额外的下游依赖,进一步拖慢启动速度),还会增加维护成本——如果你升级SDK,这个库会继续工作吗?会不会存在漏洞?而且还存在潜在的供应链攻击风险。
过去人们常常随便地包含整个库。如今,缺点更受关注,对于简单的功能,代码通常会被复制/重新封装。
备选方案
你要的其实并没有实际意义。
要解释清楚,我们先需要介绍一个概念。
传递依赖
有一种叫做“传递依赖”的概念。库就是一个项目,项目也可以有依赖。所谓传递依赖是:
- 你使用的某个库的依赖(即依赖的依赖)……
- 但你的项目并不需要知道它。
构建系统通常的工作方式是自动处理——传递依赖会在运行时同样出现,因为原因同样:如果你的某个依赖在你尝试运行时不在类路径上,你的依赖也不会工作,因此如果它的某个依赖缺失,它也不会工作。
你这个想法的根本问题
一个项目(比如你正在使用的库)显然充满了“自身内部的依赖”。而且这些依赖会不断变化,且没有统一的规范(没有明确的规格)。
在你自己的代码中,名为 A.java 的源文件有多频繁地引用整个项目中的任何其他源文件?我猜这是一个很高的数字;绝大多数项目都会报告一个非常大的数字。这些就是“在项目中的传递依赖”。
因此,如果你真的得到了你想要的——一个能把你未使用的内容“剥离”的工具,那么你实际使用的类文件之所以能够工作,是因为它们的内部依赖也在;一旦去除了这些内部依赖,它们就无法工作。
进阶思考:树摇
理论上这是一个可解决的问题:从你实际需要的代码开始,然后让某个工具对代码进行扫描,自动确定为了让你明确列出的初始类集合工作而必须存在的其他类。
这被称为“树摇”。
除非一个库被明确设计成与这一概念兼容,否则要真正实现它并不可行——例如存在服务提供者加载(service loaders)和反射加载,会让树摇算法变得困难。