美文网首页
解决大型Java项目编译依赖问题的一些思路

解决大型Java项目编译依赖问题的一些思路

作者: hxfirefox | 来源:发表于2019-02-19 23:44 被阅读12次

现实的编译依赖问题

大型项目解决编译依赖一直是个让人头痛的难题,头痛首先是因为大型项目编译依赖本身的复杂度,我们都知道一个小项目的编译依赖是比较容易管理的,很可能只是下面I这样一张关系图就可以描述,但是当项目变得庞大后,那么我们得到的可能就是II这样一张关系图了。

依赖复杂度

当我们面对II这样一张“剪不断理还乱”的关系图时,我们很难有勇气说依赖关系是清楚的、可管理的,这基本上就是求得自我心理安慰的鬼话。

其次解决编译依赖问题的投资回报率过低,在一个依赖关系已经十分“盘根错节”的项目中试图解决其依赖问题显然需要投入大量的人力和时间成本。在问题得到解决后,你自认为一切都已经步入正轨,却突然发现刚刚提交的新代码又轻轻松松地产生了编译依赖问题并且这样的代码还越来越多,这个时候你恐怕只能发出“人间正道是沧桑”的感慨了。

太令人悲伤了!

写到这里一定会有人说何不借助工具呢,比如maven,它能把依赖整理得服服帖帖的。maven确实是一个出色的依赖管理工具,通过标记dependency可以把依赖表达出来再结合中央仓储实现对依赖的自动管理,但是好用不代表可以滥用,在实际的大型项目中maven一样会让你闹心。

逐步推进的解决思路

回到前面提到的依赖复杂度的问题,如果整个项目中每个工程的依赖都是清晰,是不是整个项目就是清晰的呢?以我的亲身经历来看这个答案只能是“是又不是”,我所在的项目代码规模超过50万行,使用maven管理,单就每个工程而言所有的依赖都是清楚的,但当所有的工程叠加在一起时却让人十分迷惑,每当整个项目在进行版本切换时无法一次编译通过,需要通过若干次编译才行,这种情况必然是依赖出现了问题。

对于较大规模的项目而言,单个工程的依赖清晰只是一种外在的表现,而软件架构对依赖的影响其实是一种潜在的约束。比如,架构上A是应用B是平台,并且A和B的工程结构通常是下图形式。

常规设计

但就是这样的设计却在编译时出现平台组件B作为一个基础组件无法独立完成编译,分析原因发现A和B形成了下图的依赖,这种交叉依赖破坏了架构设计约束。

交叉

令人郁闷的是大多开发人员并不感知这种糟糕的依赖(基本上是开发过程想走捷径,实则对设计架构不太敏感),只有真正需要触碰版本升级或是编译的人才能体会到,这也导致此类依赖往往被发现得很晚,当想要纠正的时候才发现“折腾”的风险太大、改动太多。

如果想解决上面的问题,可以整理出这种依赖然后单独编译那些依赖的组件,例如可以先编译B.api然后编译A3.api,最后编译B.impl,但这时候你会发现层次化的依赖也会出现好心办坏事的问题。

工程中依赖有两种,一种是通过dependency来体现,这种依赖是平面化的;而另一种则是通过parent来体现,这种依赖是层次化的,一般的工程都会像下面一样规划依赖关系,这样当独立编译其中的模块时,位于层次化依赖路径上的parent pom是不会被编译,因此解决上面问题的独立组件编译的方法会遭遇B.impl找不到A3.api依赖的A3.parent错误,这可真是一波未平一波又起啊!

层次化依赖

根据上面的依赖有两种解决问题的思路:一种是聚合,一种则是切断。聚合是指将B.api、A3.api放在一起编译,从而在将api以及相关依赖一次编译生产,这种聚合可以是物理的也可以是逻辑的。物理聚合需重新规划工程代码的目录结构,如将项目中所有的api代码都归到一个库中,这种调整波及范围较大;逻辑聚合则可通过maven实现,网上搜索即有范例这里不再赘述。切断则是将上图的层次化依赖打破,如下图。

切断依赖

这里实际上将mavenpom作用进行了分割,一种pom是用来管理版本、依赖以及插件的,如下左图,这类pom是真正的parent是需要进行编译和部署的;而另一种则是作为容器来管理模块的,如下右图,在依赖路径上是可有可无的,因此我们在进行切断时可以选择不依赖后一种pom来去除多余的依赖。

反思与总结

从解决编译依赖问题的过程看,前后历时3周时间,大部分面临的问题非常琐碎的,需要见招拆招并且想到越深困难越多,这也是很多人避之不及的原因。再把这个过程反过来看,实际上我们经历了一遍设计是如何一点点腐化和破坏的过程,看似不起眼的小小依赖拥有惊人的破坏力,其实这不是一个技术问题而更应该看作是“破窗效应”的真实体现,我们设计的架构、原则本应成为约束自身行为的规范,但事实上如果不能在出现问题的早期就纠正错误,那么类似的问题就会越来越多。

不过如何早期发现这些依赖问题又引向了另一个更复杂的问题,这里就不展开讨论了,就目前而言比较有帮助的手段恐怕只有Code Review,但这也需要研发人员充分理解设计并严格遵循设计原则,两者缺一不可,需要有人时刻保持警惕,否则一旦踏入依赖问题的泥淖,就只能投入时间和人力去“妥协性”地解决问题。

相关文章

网友评论

      本文标题:解决大型Java项目编译依赖问题的一些思路

      本文链接:https://www.haomeiwen.com/subject/pylleqtx.html